C语言指针 《C语言点滴》读书笔记

内存的基本单位是字节(byte),每一个字节都有一个独一无二的地址。在32位的电脑中,内存中的地址都被表示成8 位16进制数。例如0x0041fa60。为了保存内存中的一个地址值,C语言需要一种特殊的变量类型,这种变量类型就是指针变量类型。整型变量保存一个整数,字符变量保存一个字符,指针变量保存一个地址。

32 位计算机上,所有的指针类型的变量都占据4 字节空间。64位的话,编译时指定好就是8字节,因为默认编译32位的程序。


char *p1,p2; 变量p2是char类型,不是char指针


*p/*p; //这会变成注释,因为有/*。*p/(*p)这么写,则结果是1

*p++=’c’是合法的,
但是(*p)++=’c’却编译出错,相当于a++=1;会出错

    char *p;
    char a[] = "abc";
    p = a;
    *p++ = 'c';//把a[0]赋值为c,然后p指针指向下一个即a[1]
    printf("%c %c %c",*(p-1),*p,*(p+1));//输出 c b c

    char *p;
    char a[] = "abc";
    p = a;
    (*p)++;//相当于a[0]++;
    printf("%c %c %c",*p,*(p+1),*(p+2));//输出b b c

    char *p;
    char a[] = "abc";
    p = a;
    printf("%c %c %c",*p,*p++,*p++);//输出c b a

    char *p;
    char a[] = "abc";
    p = a;
    printf("%c %c %c",*p,*++p,*++p);//输出c c c

第三、四部分为什么会这样呢?看了printf的源代码……没看懂


当定义一个指针的时候,如果不对它进行初始化,那么它指向的就是一个不确定的地址。这种情况下,当我们访问这个指针指向的变量的时候,程序可能就会发生问题解决野指针的办法很简单,当我们定义一个指针类型的变量的时候,顺手就在后面写上一个等号。如果暂时不知道指针该指向何处,就让它指向NULL,如short* ptr = NULL;

几种可以用来对一个xx 型的指针进行赋值的方法
•xx 型的地址

o 地址符号

o 其他xx 型指针

o xx 型数组

• 强制转换成xx 型的地址

o malloc 函数申请的内存

o 任何void 类型指针

• 空指针

o NULL
int *p1,*p2;
int a = 10;
int array[]={1,2,3};
p2 = &array[0];
p1=&a; /* 取地址运算符 */
p1=p2+1; /* 其他指针 */
p1=array; /* 数组 */
p1=(int *)malloc(sizeof(int)); /* 强制转换malloc申请的内存 */
p1=NULL; /* 空指针 */

任何指针内部其实都包含一个地址信息和一个类型(长度)信息。由于void类型指针只包含地址信息,不包含长度信息,将任何类型的指针赋值给void类型指针时,类型(长度)长度信息就丢失了。现在你就可以理解为什么对void类型指针进行算术运算和进行取值操作都是不允许的

void *vp;
int *ip = &i;
int k;

vp = ip;
vp++; /* error */
k = *vp; /* error */

与此相反,如果将void 类型指针赋给其他类型的指针,则需要进行强制类型转换,使其包含类型(长度)信息

ip =(int *)vp;
ip++; /* ok */
k = *ip; /* ok */

void类型指针一般用在函数的参数和返回值中,以达到泛型的目的,目的就是使一个函数能够处理更多的类型。

void * malloc ( size_t size );
void * memcpy(void *dest, const void *src, size_t len);
void * memset ( void * buffer, int c, size_t num );

malloc 返回的就是一个void 类型指针,同理,memcpy 和memset 两个函数也使用void类型指针来达到泛型的目的。这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,并不关心这片内存中保存的是什么类型。

void 类型指针虽然可以用来达到泛型的目的,但是它也包含一些风险,这种风险主要是丢失了类型的信息。

void foo1(int* pi){
float *pf = pi /*编译出错*/
}

但是

void foo2(void* pi){
float *pf = (float*)pi /*编译通过*/
}
int i= 5;
foo2(&i)

使用pf指针的时候,它指向的地址并不包含你期望的一个浮点数,所以就会产生一些非常奇怪的错误。这种运行时的错误也是非常难以查找和排除的。比如上面的如果去输出*pf,会输出0.000000


xx 型数组变量代表一个xx 型地址,我们把这个真理叫做数组真理。

    int a[6] = { 0, 1, 2, 3, 4, 5 };
    printf("%d %d", a[0], *(a+1));//输出0 1

In evaluating a[i], C converts it to *(a+i) immediately

也就是说,当a[i]用在一个表达式中的时候,编译器自动地将其转换为指针加偏移量(a+i)的形式.所以a[i]这种书写方式,只是为了给程序员书写源代码的时候准备的.
所以a[0]和0[a]是等价的,翻译后都是*(a+0)

但是数组和指针还是有区别的。例如,当sizeof 或者取地址运算符&作用于数组变量a 的时候,sizeof(a)会返回整个数组的长度、而不是一个指针的长度。

  • 你不能改变数组a的地址,所以a++;这个语句是不允许的。但是pa++;却没有问题。因为指针可以改变指向

  • 当指针pa 指向一个单独的变量b 的时候,指针的行为就和数组a 没有一点相似的地方了。例如,你再也不应该写出pa[1]这样的语句,否则就会去访问紧挨着变量b 的一块内存,谁也不能确定那块内存的内容和意义,所以这种行为是非常危险的。

  • 如果你在一个文件中定义了int a[5];,在另外的文件中却声明为extern int *a;,这样也是不对的。

“pointer arithmetic and array indexing are equivalent in C, pointers and arrays are different”
字符指针和字符数组还有一些不同,放在后面。


程序10-9 指针型指针和指针数组

char *a[]={"zhao","yan","is","a","good","teacher",NULL};
char **p;
for(p=a;*p!=NULL;p++)
{
printf("%s\n",*p);
}

程序10-9也演示了指针型指针和指针数组之间紧密的联系。模拟一个锯齿形状的二维数组。保存多个长度不相等的字符串正是这种锯齿形状二维数组的典型应用。通常,把数组中最后一个指针设为NULL 可以标记数组的末尾状态。
wow,好巧妙这里。

如何定义一个数组型的指针?这个写法就有点讲究了。以int 类型为例,我们不能写成int *p[3],这是因为“[]”的优先级比“*”号高,所以int *p[3]会被编译器解释为int *(p[3])。这是一个数组,数组中每一个元素是一个指针,这其实就是上面介绍过的指针数组,而不是我们要定义的数组指针。为了解决这个问题,我们用括号改变它的优先级,写成int (*p)[3]。这时(*p)是一个指针,指针指向的类型为int[3],这是一个一维数组型变量,符合我们的定义。

有了int (*p)[3]=array[2][3]的写法,我们对二维数组也可以利用指针来进行访问了,如下面所示。
• p 即 &array[0]
• p+i 即 &array[i]
• *(p+i) 即 array[i]
• *(p+i)+j 即 &(array[i][j])
• *(*(p+i)+j ) 即 array[i][j]

如果用数组型指针

    char c[4][9] = { "I'm", "Wen", "Ruicheng",NULL};
    char(*b)[9] = c;
    for (; *b[0]!=0; b++)
        printf("%s", *b);

*b[0]!=0 是因为b是一个char[9],不是一个char ,不能直接NULL或者”” 所以如果是输出距离不等的字符串,还是指针型数组方便。
指针型数组首先是一个数组,每个元素都是指针。在以下代码,每个元素都是char指针。

    char *a[] = { "I'm", "Wen", "Ruicheng", NULL };
    printf("%s %s %s\n", a[0],a[1],a[2]);
    char **p;
    for (p = a; *p; p++)
        printf("%s ", *p);

未完待续

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值