一、指针与地址

一元运算符&可用于取一个对象的地址

一元运算符*是间接寻址运算符。当它作用于指针时,将访问指针所指向的对象

实现指针指向的内容加1:(ptr)++ ,因为类似和++这样的一元运算符遵循从右至左的结合顺序。

二、指针与函数参数

c语言的函数参数是值传递,所以要修改原变量就要用到指针

三、指针与数组

通过数组下标所能完成的任何操作都可以通过指针完成,而且一般来说,用指针比用下标运行更快

int arr[10];
int *ptr;

如果ptr指向数组的某个特定元素比如赋值ptr = &arr[1],那么根据指针运算的定义,ptr+1将指向下一个元素,ptr+i将指向下i个元素,ptr-i将指向之前的第i个元素。无论数组的类型和长度是什么,上述结论都成立。

数组名就代表该数组第一个元素的地址。对数组元素arr[i]的引用同样可以写成*(arr+i)

数组名虽然可以当作地址用,但数组名和指针有一个不同之处。即指针是变量,所以可以做赋值或自增等运算,而数组名不是变量,尝试向数组名赋值或自增数组名会报错。

四、字符指针与函数

"I am a string"字符串常量在c语言内部是用字符数组存储的,而编译器会在这个字符数组的最后加上\0标志字符串的结束。因此字符串常量占据的存储单元也因此比双引号内的字符数大1。

语句char *pmessage="Hello" 将一个指向字符串的指针赋值给pmessage,本质上只进行了指针的操作。

区分以下两条定义

char amessage[] = "Hello";
char *pmessage = "Hello!";

这两个定义有很大的差别,第一条声明了一个字符数组用以存放字符串和结尾的\0,数组中的字符可以修改,但根据3.4所述,amessage不能被赋值,即不能将amessage指向其他地方。但第二条,定义了一个指向字符的指针,让它指向一个字符串常量,所以在这里不能修改字符串的内容,但可以让这个字符指针pmessage指向其他地方。
而当这两种定义方式用在函数的形参时,又会有不一样的问题。

void fun(char* s);
void fun(char s[]);

这两种函数的定义方式其实没有任何区别。由于c语言的函数传参是值传递,所以不论fun函数的实参是数组名还是指针,最终传递的都是地址,所以不论形参的定义是char* s 还是char s[] ,s其实都是指针,所以两种定义方式没有区别。

运用所学的知识实现strcpy函数:

// strcpy: copy t to s
void strcpy(char *t, char *s) {
    while((*s = *t) != '\0') {
        s++;
        t++;
    }
}

简化版:

// strcpy: copy t to s; version 2
void strcpy(char *t, char *s) {
    while(*s++ = *t++);
}

五、指针数组以及指向指针的指针

初始化一个字符指针数组,每个指针指向下标对应的月份的名字的字符串。

char* arr[] = {
    "Illegal month", "January", "February", "March",
    "April", "May", "June", "July", "August",
    "September", "October", "November", "December"
};

六、指针与多维数组

区分多维数组与指针数组
有如下定义:

int a[10][20];
int* b[10];

从语法角度讲,a[3][4]b[3][4]都是对一个int对象的合法引用。但a真真切切的分配了200个int的空间,而b只分配了10个指针的空间,如果b里的每个指针都指向一个20个元素的int数组,则a和b就几乎一样了。用指针数组完成多维数组的功能有一个好处,就是数组的每一行可以是长度不同的,就像第5节的月份字符数组一样,每一行都是不同长度的月份名字符数组。

七、命令行参数

main函数的完整声明如下:

int main(int argc, char const *argv[])

在支持c语言的环境中,可以在程序开始执行时将命令行参数传递给程序。调用主函数main时,它带有两个参数。第一个参数argc的值表示运行程序时命令行中参数的数目;第二个参数argv称为参数向量,它是一个指向字符指针的数组,其中每一个指针指向的字符串对应一个参数。

按照c语言的约定,argv[0]的值是启动该程序的程序名,因此argc的值至少为1。

八、Tips

const修饰指针

int const * ptr;
int * const ptr1;

两种情况,const在左边和const在右边。
当const在*左边时,表示不能通过ptr修改所指向变量的值。
当const在*右边时,表示ptr不能再指向其他变量。

const修饰二级指针

int a = 10;
int *ptr = &a;
int const * const * const ptrr = &ptr;  // 二级指针的const

第一个const使得不能通过ptrr两次解引用改变a的值
第二个const使得不能通过ptrr一次解引用改变ptr的指向
第三个const使得ptrr不能再指向其他int*

最后修改:2019 年 11 月 10 日
如果觉得我的文章对你有用,请随意赞赏