最近看C相关的代码,总是很吃力,所以决定看看C Primer Plus这本书,这里对指针,字符串这些常用的部分做一个笔记
sizeof,指针,数组
- 看代码:
//sum_arr1.c --数组元素之和
#include<stdio.h>
#define size 10
int sum(int ar[], int n);
int main(void)
{
int marbles[size] = { 20, 10, 5, 1, 22, 18, 18,123, 123,12 };
long answer;
answer = sum(marbles,size);
printf("the totole number of marbles is %ld.\n",answer);
printf("the size of marbles is %ld.\n", sizeof marbles);
return 0;
}
int sum(int ar[], int n)
{
int i;
int total = 0;
for(i = 0; i < n; i++)
total += ar[i];
printf("the size of ar is %ld.\n", sizeof ar);
return total;
}
- sizeof是以字节为单位,所以10个int的数组的size是40
- ar长是8,说明指针地址长度是8个字节,即48位,这个计算机有关
- sizeof 数组 和 sizeof指针的区别
- 指针+1 ,指的是+1个存储单位,对于marbles数组来说是4个字节,即指针+1,地址大小+4
- 对于c,
ar[i]
和*(ar+i)
这两个表达式等价。无论ar是数组名还是指针变量,这个两个表达式都没有问题;然而只有当ar是指针变量,才能用ar++
这种表达式。
指针操作
- demo
//ptr_ops.c -- 指针操作
#include<stdio.h>
int main(void)
{
int urn[5] = { 100, 200, 300, 400, 500};
int *ptr1, *ptr2, ptr3;
ptr1 = urn; //把一个地址赋给指针
ptr2 = urn[2];
printf("pointer value , dereference pointer, pointer address:\n");
printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
//指针加法
ptr3 = ptr1 + 4;
printf("\n adding an int to a pointer:\n");
printf("ptr1 + 4 = %p, *(ptr1 + 4) = %d", ptr1 + 4, *(ptr1 + 4));
//递增指针
ptr1++;
printf("\n value after ptr1++ \n");
printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
//递减指针
ptr2--;
printf("\n value after ptr2-- \n");
printf("ptr2 = %p, *ptr2 = %d, &ptr2 = %p\n", ptr2, *ptr2, &ptr2);
--ptr1;//恢复指针
--ptr2;
printf("\n pointers reset to original values\n");
printf("ptr1 = %p, ptr2 = %p\n", ptr1, ptr2 );
}
- 注意:
double *pd;
*pd = 2.4
- 这样用错误,因为创建一个指针,未初始化时,系统只分配了储存指针本身的内存,并未分配储存数据的内存,即指针的值,因此,在使用指针之前,必须先用已分配的地址初始化它;而此处第二行的意思时把2.4存在pd指向的位置,但是pd未初始化,所以不知道存在哪里
关于const
对形参使用const:
int sum(const int ar[]);
const是为了告诉编译器,不能修改ar指向的数组中的内容,如果修改了,会编译器会捕获并抛出错误。这是为了让我们在给函数传参时保护数组数据不被改变。
void show_array(const double ar[], int n);
void mult_array(double ar[], int n)
如上面的声明,只是显示数组,那么不需要改变,传参加const;但是修改数组,需要改变,则不加;
const与指针赋值
- 把const或非const数据的地址赋值给指向const的指针是合法的:
double rates[5] = {88.99 , 110.2, 123.1, 200.4, 210};
const double locked[4]={0.1, 0.2, 0.5, 0.9};
const double *pc = rates; //有效
pc = locked; //有效
pc = &rates[3];//有效
- 然而,只能把非const数据的地址赋值给普通的指针,
double *pnc = locked; //无效
因为,如果可以的话,那么普通指针可以改变const数组的数据了。
- 也不要把const数组当作实参传递给mult_array这样的函数
mult_array(rates,5);//有效
mult_array(locked,5);//不可以
因为使用形参修改const数据的后果未知
- 注意
* const pc
double * const pc = rates; //指向数组开始
pc = &rates[3]; //不可,const指针不可改变指向
*pc = 12.1; //可以,可以更改const指针指向的值;
数组和多重指针
- 看一个例子:
/*zippo1.c -- 指针和多维数组*/
#include<stdio.h>
int main(void)
{
int zippo[4][2] = { { 2, 4 }, {6,8},{1,3},{2,0}};
printf(" zippo = %p, zippo + 1 = %p\n", zippo, zippo + 1);//zippo是两
个int组成的一维数组的地址,所以+1,相当于地址+8
printf(" zippo[0] = %p, zippo[0] + 1 = %p\n", zippo[0], zippo[0] + 1);//zippo[0] 是一个int的地址,所以+1,即地址+4, 你可以类比一维数组char ar[2];ar是一个char的地址,char[0]才是该地址的值,所以zippo[0]是两个Int组成的一维数
组的地址,那么zippo就是这个地址的地址
printf(" *zippo = %p, *zippo + 1 = %p\n", *zippo, *zippo + 1);//zippo的解引用应该是zippo[0]的地址,+1应该是地址+8
printf("zippo[0][0]=%d\n", zippo[0][0]);
printf(" *zippo[0]=%d\n", *zippo[0]);// *zippo[0]指的是zippo[0]指向的一
个int的解引用,那么还是首地址对应的一个int
printf(" **zippo = %d\n", **zippo);// zippo是地址的地址,所以必须解引用
两次才能获得原始值,即zippo[0][0]
printf(" zippo[2][1] = %d\n", zippo[2][1]);
printf("*(*(zippo+2)+1) = %d\n",*(*(zippo+2)+1) );
return 0;
}
- :
- 二维数组zippo的地址和一维数组zippo[0]的地址相同,他们的地址都是各自数组首元素的地址,因而与
&zippo[0][0]
也相同 - zippo[0]指向一个四字节的Int,所以+1,地址+4;zippo是一个内含2个int类型值的数组的地址,所以指向一个8字节的数据对象,所以+1,地址+8(相当于zippo是一个储存单位是8字节的一维数组)
- zippo[0]与*zippo完全相同。对二维数组名解引用两次,得到储存在数组中的值,和数组使用两个[][]得到数组中的值对应
- 所以zippo[2][1]与
*(*(zippo+2)+1)
是对应的,所以说用数组更直观 - 考虑
int (*pz) [2]
:pz是一个指向内含两个int类型的数组。其实不用想太复杂,知道pz的储存单位是8个字节,*解引用与[]等价即可:
pz+1 即地址+8
pz[0] + 1 ,pz解一层引用即成为了指向int的地址,+1,即+4
*pz + 1同理
pz[0][0]与 *pz[0] **pz表示的都是首地址数据
pz[2][1] 与 *(*(pz+2)+1)等价
-
多维数组的函数声明
int sum(int ar[][4]) //正确 int sum(int ar[][])//不可 int sum(int ar[3][4])//3会被忽略
- 这样可以传入变长数组,第一个[]内可是1,2.。。
-
复合字面量:
int sum(const int ar[])
...
int total3;
total3 = sum((int []){1,2,3,4,5,6});