C语言学习(7)

一、C语言指针

1. 一维数组和指针的关系

  第一种: int类型指针和int类型数组之间的关系

  第二种: char类型指针和char类型数组之间的关系

  规则: C语言规定,数组名出现在表达式中,代表该数组首元素的地址(数组名就是个指针,指向首元素的指针)

      C语言规定,数组名取地址出现在表达式中,代表指向整个数组的一个指针,称作数组指针

#include <stdio.h>

int main()
{
    //int a[10];
    char a[22];
    //doube a[8];
    printf("a的地址:%p\n", a);
    printf("a+1的地址:%p\n", a + 1);
    printf("&a的地址:%p\n", &a);       //int (*)[10]
    printf("&a+1的地址:%p\n", &a + 1); //增加40字节
    printf("&a[0]的地址:%p\n", &a[0]);
    printf("&a[0]+1的地址:%p\n", &a[0] + 1);
}

 

  总结: 一维数组和指针之间的关系有如下几种常见写法

  写法一: char buf[10]="hello";

       char *p=buf;

      *(p+i); //i -->0到strlen(buf)

  写法二: char buf[10]="hello";

       char *p=buf;

       p[i]; //i -->0到strlen(buf)

#include <stdio.h>
/*
    %s -->要求后面是字符串的起始位置(指针)
    %c -->要求是普通变量的名字即可(不要求是指针)
    %d -->要求是普通变量的名字即可(不要求是指针)
*/
int main()
{
    char buf[10] = "hello";
    int otherbuf[10] = {78, 56};
    char *p = buf; //&buf[0]
    printf("%s\n", buf);
    printf("%s\n", &buf[0]);
    printf("%s\n", p); //%s就是通过首地址,自动把整个字符串完整输出
    printf("%s\n", &buf[2]);

    printf("%c\n", buf[0]);
    printf("%c\n", *p);
    int *q = otherbuf; //&otherbuf[0]
}

 

 

2. 野指针

  目前为止,C语言保持多个相同类型的数据有如下几种方法:

  方法一:数组

  方法二:指针

  定义了指针,但是这个指针没有明确的指向,是不能使用的(很危险,容易产生段错误)

  #include <stdlib.h>

  void *malloc(size_t size); //给指针分配size个字节的内存空间,不会自动把内存空间清零

  char *p=malloc(20); //给指针p分配20字节的内存空间

  int *p=malloc(20); //给指针p分配20字节的内存空间

  void free(void *ptr); //把之前操作系统分配给你内存空间释放(把内存还给操作系统)

  free(p);

  void *calloc(size_t nmemb, size_t size); //分配内存空间同时会自动把内存空间清零

  char *p=calloc(5,10); //申请分配5块内存空间,每一块的大小是10个字节,总共分配5*10=50字节的空间

  int *p=calloc(10,4); //10块内存空间,每一块大小是4字节

  void *realloc(void *ptr, size_t size); //重新分配内存空间,改变之前分配的内存空间的大小

  realloc(p,100); //重新给p申请100个字节的存储空间

 

 

 

注意:重新分配的内存首地址可能跟原来的地址一样,也可能不一样

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    int i;
    //用法一:char类型指针指向单个字符的地址
    char a = '@';
    char *p = &a; //定义指针p指向a的首地址

    //用法二:char类型指针指向一个字符串的首地址(实际开发中用的最多)
    //第一种: 定义指针q,q指向字符串常量hello的起始地址
    //char *q="hello";
    //printf("q指向的字符串是:%s\n",q);
    //printf("q指向的是字符h在内存中的地址:%c\n",*q);

    //第二种: 定义指针q,指向char数组的首地址
    //char buf[10]="world";
    //char *q=buf;  //数组名buf就是个指针,等价于 &buf[0]
    //char *q=&buf[0];
    //printf("q指向的字符串是:%s\n",q);
    //printf("q指向的是字符串的起始地址:%c\n",*q);
    //传统做法,遍历数组通过下标来实现
    //有了指针之后,通过指针来遍历访问数组成员了
    //写法一:
    //for(i=0; i<strlen(buf); i++)
    //{
    //printf("%c\n",*(q+i));  //利用了指针的加法
    //}
    //写法二:
    //while((*q)!='\0')
    //{
    //printf("*q is:%c\n",*q);
    //q++;
    //}

    //第三种: 定义指针,没有分配内存空间,就直接使用指针
    //char *q; //q指向哪里不清楚,随机的,是个野指针
    char *q = malloc(20); //分配20字节的空间给指针q
    printf("q指向的地址是:%p\n", q);

    printf("请输入一个字符串!\n");
    scanf("%s", q); //很危险,万一q后面的地址非法,就完蛋了
    printf("你输入的字符串是:%s\n", q);
    //释放刚才申请的内存空间
    free(q);
    return 0;
}

 

 

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int i;
    int *p = malloc(16); //16个字节空间,不会自动清零
    *p = 56;
    *(p + 1) = 96;

    int *q = calloc(4, 4); //4块内存空间,每一块4字节,会自动清零
    for (i = 0; i < 4; i++)
    {
        printf("*(p+%d) is:%d\n", i, *(p + i));
        printf("*(q+%d) is:%d\n", i, *(q + i));
    }

    //发现p指向的16字节空间不够用,想重新分配多一点
    p = realloc(p, 160);
    printf("原本存放在p里面的内容也复制过来:%d\n", *p);
    printf("原本存放在p里面的内容也复制过来:%d\n", *(p + 1));

    //发现q指向的16字节空间多了,想重新分配少一点
    q = realloc(q, 4);

    //释放
    free(p);
    free(q);
}

 

3. 指针的加法和减法运算

  指针的加法运算:(指针加上一个整数)

  两个指针直接相加 --》没有任何实际意义,面试题也不会这么写

  char buf[10];

  char *p=buf; //指向首地址

  p+1; // 初学者有错误的认识,把地址当成是数字+1

  总结:指针做加法运算,加的是类型的大小

  指针的减法运算:(指针减去一个整数)

  两个指针直接相减---》只能是在数组里面两个指针相减有意义

  计算公式: (p-q)结果是: (p-q)/指针类型的大小

  总结:指针做减法运算,减的是类型的大小

#include <stdio.h>
/*
    指针的加法运算,加的是指针类型的大小
*/
int main()
{
    int a = 9;
    double b = 78;
    char c = 'u';

    int *p1 = &a;
    double *p2 = &b;
    char *p3 = &c;

    printf("p1保存的地址:%p\n", p1);   //int -->4字节
    printf("p1+1的地址:%p\n", p1 + 1); //+4个字节

    printf("p2保存的地址:%p\n", p2);   //double -->8字节
    printf("p2+1的地址:%p\n", p2 + 2); //+16字节

    printf("p3保存的地址:%p\n", p3);   //char -->1字节
    printf("p3+1的地址:%p\n", p3 + 1); //+1字节
}

 

4. 空指针和void类型的指针

  空指针: 在C语言中用NULL表示,在系统中NULL就是0x0000 0000地址

  用法一:判断函数的返回值(返回指针),是否正确

  void *malloc(size_t size);

  char *p=malloc(10); //p可以存放10个字符

  if(p==NULL) //说明malloc申请10个字节空间失败了

  else //说明申请成功

  int *q=malloc(20); //q可以存放5个整数

  用法二:定义一个指针的时候,先让它指向NULL,后面改变指向

  int *p=NULL; //p是个野指针,p由系统随机指向

  p=&a;

  scanf("%d",p)

  void类型的指针: C语言表示通用类型指针

  void类型的指针可以转换C语言其它类型的指针

  void *malloc(size_t size); //void *通用性比较强

  char *p=malloc();

  int *p=malloc();

  double *p=malloc();

 

5. 一维数组跟指针有关的几种写法

  int a[5];

  a //指针 int *

  &a //指针,指向整个数组的一个指针(称作数组指针) int (*)[5]

  &a[0] //指针 int *

  a[0] //普通变量,不是指针

 

6. 数组指针和指针数组

数组指针:中心词是指针

  定义: int a[5]; ---》类型 int [5]

  int (*p)[5];

  p=&a;

  char b[9]; --》 类型 char [9]

  char (*p)[9];

  p=&b;

  指针的一般定义:

  公式: 类型 *名字

  int *p;

  char *p;

  数组指针也符合这个公式

  int a[100];

  int (*p)[100]=a; //左右两边指针类型不一致

  数组指针 int类型指针

  int (*p)[100]=&a; //左右两边指针类型一致

  数组指针 数组指针

 

#include <stdio.h>

int main()
{
    /*     int (*p)[5];  //数组指针
    int *q[5];    //指针数组 */
    //最显著的区别--》数组指针,指针名字跟*加了圆括号

    //定义int*类型的指针数组
    int n1 = 48;
    int n2 = 89;
    int n3 = 96;
    int *a[5] = {&n1, &n2, &n3};
    //  a[0] --》&n1

    printf("n1的地址:%p\n", a[0]);
    printf("n1的值:%d\n", *(a[0]));

    //定义char*类型的指针数组(存放多个字符串的首地址)
    char buf1[10] = "china";
    char buf2[10] = "hello";
    char buf3[10] = "world";

    char *b[3] = {buf1, buf2, buf3};
    printf("%s\n", b[0]);
}

 

 

指针数组:中心词是数组

  指针数组:本质上还是数组,只不过这个数组里面存放的全部都是相同类型的指针

  前面学习的数组,里面存放的都是相同类型的普通变量

  数组的一般定义:

  公式: 类型 数组名[数组元素个数]

  int a[10];

  int * b[10]; // 定义了一个最多可以存放10个int *的数组

  char *c[10]; //定义了一个最多可以存放10个char*的数组

作业:

1.

char *p;

int *p;

double *p sizeof(p) sizeof(*p) //看看大小是多少,为什么??

32位的ubuntu系统 ---》全部都是4个字节

64位的ubuntu系统 ---》全部都是8个字节

原因: 在64位系统上所有的地址长度都是8字节,而指针里面存放的就是其他变量的首地址,所以指针大小全部都一样

答:在32位系统计算机中则为4!
    在64位系统计算机中则为8!
因为sizeof求得是所占的Byte数;
而p在该系统中内存随机分配,但是在不同系统中所占的位数不一样
所以说,sizeof(p) p是地址,地址位数在不同的系统中位数一样
若是sizeof(*p) *p解引用了对应的类型,所以所占的位数也不一样
char是1位字节
int是4位字节
double是8位字节
View Code

 

2.

char buf[10]=''gecchina";

char *p=&buf[3];

p=p+2;

*p='#';

printf("buf is:%s\n",buf);

答:最后输出为buf is:gecch#na
因为一开始*p的地址指向的是该数组的3位上,对应的字符为c,
后经过指针的加法运算,加多了2位,此时*p的地址指向为
该数组的5位上,对应的字符为i,经过解引用赋值,
将该字符替换了#,最后输出为gecch#na
View Code

 

3.

char buf[5][10]={"hello","world","china"};

char *p=&buf[1][3]; //world中l的地址

char *q=&buf[2][1]; //china中h的地址

printf("q-p is:%d\n",q-p);

总结: 指针相减表示它们之间间隔了多少个数据成员

答:最后输出为8。
*p的指针指向该数组的1,3 上,该字符为l
*q的指针指向该数组的2,1上,该字符为h
假如该数组的首地址为0,这个数组总共有50位,即0-49;
则*p指的地址是该数组的12位;
*q指的地址是该数组的20位;
输出做了个地址间的减法,则为20-12=8
View Code

 

posted @ 2020-03-09 16:31  Geek_Jian  阅读(212)  评论(0编辑  收藏  举报