C语言——指针进阶

概括图:

C语言——指针进阶_第1张图片

一.指针进阶。

 

A.指针与常量。(重点const作用于哪个域)

1.指向常量的指针(指针常量):这种指针自身值不能被修改,但指针可以修改指向。

例:

#include 

int main()
{
	int num = 123;
	int const cnum = 111;	//只读变量
	int const *p = &cnum;	//指向只读变量的只读指针
	
	printf("cnum: %d, &cnum: %p\n", cnum, &cnum);
	printf("*p: %d, p: %p\n", *p, p);
	
	*p = 1235;	//自身值无法改变,这句无法通过。
	
	p = num;		//指向可以改变,这句可以通过。
	printf("cnum: %d, &cnum: %p\n", cnum, &cnum);
	printf("*p: %d, p: %p\n", *p, p);
	
	num = 110;	//这样就改变了p的值。
	printf("cnum: %d, &cnum: %p\n", num, &num);
	printf("*p: %d, p: %p\n", *p, p);
	
	return 0;
}

 

 

 

#注意:

建议const写在后面:

A. const所修饰的类型是正好在它前面的那一个。
B. 我们很多时候会用到typedef的类型别名定义。比如typedef char* pchar,如果用const来修饰的话,当const在前面的时候,就是const pchar,你会以为它就是const char* ,但是你错了,它的真实含义是char* const。是不是让你大吃一惊!但如果你采用const在后面的写法,意义就怎么也不会变,不信你试试!

 

2.指向非常量的常量指针(常量指针):这种自身值可以修改,但指向不能被修改。

例:int *const p = #    //const在星号左边是指向常量的指针,修饰*自身值无法改变指向可以改变、放在右边就是常量指针,修饰p自身值可以改变指向不能改变。

例:

#include 

int main()
{
	int num = 123;
	int const cnum = 111;	//只读变量
	int * const p = &cnum;	//常量指针
	
	*p = 1024;	//自身值可以改变
	printf("*p: %d\n", *p);
	
	num = 110;	//改变了p指向的数据的值。
	printf("*p: %d, num = %d\n", *p, num);
	
	p = &cnum; //指向不能改变,这句无法通过。
	
	return 0;
}

 

3.指向常量的常量指针:指针自身值不能被修改,同时指针指向也不能被修改。

例:

#include 

int main()
{
	int num = 123;
	int const cnum = 111;	//只读变量
	int const * const p = &cnum;	//指向常量的常量指针。
	
	*p = 1024;	//自身值不改变,这句无法通过。	
	p = &cnum; //指向不能改变,这句无法通过。
	
	return 0;
}

 

 

 

4.指向"指向常量的常量指针"的指针:int const * const *pp = &p;

例:

 

#include 

int main()
{
	int num = 123;
	int const cnum = 111;	//只读变量
	int const * const p = #	//指向常量的常量指针
	int const * const *pp = &p; //指向"指向常量的常量指针"的指针
	
	printf("pp: %p, &p: %p\n", pp, &p);
	printf("pp: %p, p: %p, &num: %p\n", *pp, p, &num);
	printf("**pp: %d, *p: %d, num: %d\n", **pp, *p, num);
	
	return 0;
}const cnum = 111;	//只读变量
	int const * const p = #	//指向常量的常量指针
	int const * const *pp = &p; //指向"指向常量的常量指针"的指针
	
	printf("pp: %p, &p: %p\n", pp, &p);
	printf("pp: %p, p: %p, &num: %p\n", *pp, p, &num);
	printf("**pp: %d, *p: %d, num: %d\n", **pp, *p, num);
	
	return 0;
}

 

 

B.指针与数组。(要明白数组与指针,自身的指向与其自身的值)

1.指向数组的指针(相同点)。

可以相互代替。

例:

#include 

int main()
{
	char str[] = "char";
	char *p = str;//直接指向数组名,也可以*p=&a[0];
	int i;
	
	for (i = 0; i < 3; i++)
	{
	printf("str[i]= %c\n", *(p+i);/*(p+1)是指指向数值的下一个元素,
	而不是用p加上数组的间隔(char占1字节空间,double占8个字节空间,int占4个)。*/
        printf("a[i]= %d\n", *(a+i);//因为数组名是数组第一个元素的地址。
	}
	//还可以用指针定义一个数组,再用下标法去访问这个数组。
	char *str1 = "This is a string of strings";
	int length;
	
	length = strlen(str1);
	
	for (i = 0;i < length; i++);
	//这里可以省略变量length但会增加效率for (i = 0;i < strlen(str); i++);,因为每次循环都要调用函数。
	{
		printf("%c\n", str1[i]);//定义指针用数组下标法访问。
	}
	return 0;
}

1.2指向数组的指针(不同点)。

数组是一个地址常量而指针是左值。

 

例:

#include 

int main()
{
	char str[] = "This is a string of strings";
	int count = 0;
	
	while (*str++ != '\0')
	{
		count++;
	}
	printf("总共有%d个字符!\n", count);
	
	return 0;
}
/*
以上代码无法编译通过,因为在循环条件时str[]不满足左值条件
str[]它是一个地址常量。
更改方法:添加个字符指针,用字符指针取代替循环时的条件左值。
*/

2.指针数组与数组指针(区分方法看名字最后两个字)

 

int *p1[5];    //指针数组,先运算p1[5]数组再是指针。
int (*p)[5];   //数组指针,先运算(*p)指针再是数组。  

 

指针数组:

C语言——指针进阶_第2张图片

总结:指针数组是一个数组,每个数组元素存放一个指针变量。

数组指针:

C语言——指针进阶_第3张图片

结论: 指针数组是一个指针,它指向的是一个数组。

注意:

int temp[5] = {0};
int *p =temp;   //它指向的是一个数组的第一个地址。

int (*p)[5] = &temp[5];  //它应该指向其数组。或者&temp。&temp=*p,循环解引用*(*p +i)。

3.指针与多维数组

注意:(1).指针与数组的下标增加或者减少是指指向下一个元素或者上一个元素。

          (2).数组名与指针名存放的值是这个元素的地址。所以用指针符号(*)取出数组名称的值是取的其数组元素的地址。

          (3).区别指针与数组的跨度。

例:

 
#include 

int main()
{
	int array[4][5] = { 0 };
	int i, j, k = 0;

	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 5; j++)
		{
			array[i][j] = k++;
		}
	}

	printf("*(array+1): %p\n", *(array + 1));//array加一后的值,相当于array[1][0]的地址
	printf("array[1]: %p\n", array[1]);//array[1]的值但打印要求%p地址。
	printf("&array[1][0]: %p\n", &array[1][0]);//取其array[1[0]的地址
	printf("**(array+1): %d\n", **(array + 1));//第一次解析array+1的值得到arraay[1[0]元素的地址。再解析得到其值。
	printf("*(array+1)+3: %pn", *(array + 1) + 3);//相当于array[1][3]的地址。
	printf("*(*(array+1)+3): %d\n", *(*(array + 1) + 3));//取得array[1][3]的值。
	printf("array[1][3]: %d\n", array[1][3]);
	getchar();
	return 0;
}

 

多维数组与指针的演变:

*(array+i) == array[i][]
*(*(array+i)+j) == array[i][j]
*(*(*(array+i)+j)+k) ==array[i][j][k]

即可以把刚刚那个例子的二维数组用数组指针指向:int (*p)[3] = array;

如: 

 

#include 

int main()
{
	int array[2][3] = { { 0, 1, 2 }, { 3, 4, 5 } };
	int(*p)[3] = array;

	printf("**(p+1): %d\n", **(p + 1));
	printf("**(array+1): %d\n", **(array + 1));
	printf("array[1][0]: %d\n", array[1][0]);
	printf("*(*(p+1)+2): %d\n", *(*(p + 1) + 2));
	printf("*(*(array+1)+2): %d\n", *(*(array + 1) + 2));
	printf("array[1][2]: %d\n", array[1][2]);

	getchar();
	return 0;
}

 

 

4.指向指针的指针:双重寻址,一层一层解析得到其值。

例:

 

#include 
int main()
{
	int num = 123;
	int *p = #
	int **pp = &p;  //双层解引用,得到num的值。
	//*a=p的地址,**a=解引p的地址。

	printf("num: %d\n", num);
	printf("*p: %d\n", *p);
	printf("**pp: %d\n", **pp);
	printf("&p: %p, pp:%p\n", &p, &(*pp));
	printf("&num: %p, p: %p, pp: %p\n", &num, p, *pp);//a指向的是num的地址,而a的值是p地址。解析a的值得到的就是p所指向的地址。
	getchar();
	return 0;
}

 

4.1指针数组和指向指针的指针

(指针数组是数组中存放每个指针的地址,我们用指向指针的指针去访问数组中每个指针所指向的值。)

优势:1.避免重复分配内存。2.只需要进行一处修改。

例:

 

#include 
int main()
{
 char *cBooks[] =
 {
  "《C程序设计语言》",
  "《C专家编程》",
  "《C和指针》",
  "《C陷阱与缺陷》",
  "《C Primer Plus》"
 };
 char **have;
 char **Classic[3];
 int i;
 have = &cBooks[4];//双层解引用去访问指针数组。
 for (i = 0; i < 4; i++)
 {
  Classic[i] = &cBooks[i];
 }
 printf("我所拥有的书是:%s\n", *have);
 printf("C经典书籍有:\n" );
 for (i = 0; i < 4; i++)
 {
  printf("%s\n", *Classic[i]);
 }
 getchar();
 return 0;
}

 

不能用指向指针的指针指向二维数组,只能用数组指针。因为二维数组加一的跨度与与指向指针的指针加一的跨度不一样。

例:

 

array[2][2];
**p = array;
p+1不等于array+1

 

为什么指向指针的指针不能与数组指针搭配哪?因为他们的下标加一的地址不同。

 

C.指针与函数

1.指针函数,是一个函数,使用指针变量作为函数返回值。常用于字符串。

修饰的是函数的返回值类型char *getchar(char);

注意:不要返回局部变量的地址。(作用域)

例:

#include 

char *getWord(char);

char *getWord(char c)
{
	switch(c)
	{
		case 'A': return "Apple";//它返回的是A的地址,后面以此类推。
		case 'B': return "Banana";
		case 'C': return "Cat";
		case 'D': return "Dog";
		default: return "None";
	}
}

int main()
{
	char input;
	
	printf("请输入一个字母:");
	scanf("%c", &input);
	
	printf("%s\n", getWord(input));//这里要求返回的是字符串。
	
	return 0;
}

2.函数指针,是一个指针,指向函数的指针。(函数名也是指针变量。当你创建go(int speed)函数时,也会创建go的指针变量,变量中保存了函数的地址。调用此函数相当于在使用此函数指针。)

定义个函数指针在给函数指针传入一个函数名就可以调用了。 int (*getint)();

例:

 

#include 

int square(int);

int square(int num)
{
	return num * num;
}

int main()
{
	int num;
	int (*fp)(int);
	
	printf("请输入一个整数:");
	scanf("%d", &num);
	
	fp = square;//也可以fp = □
	
	printf("%d * %d = %d\n", num, num, (*fp)(num));//也可以fp(num)
	
	return 0;
}

 

2.1函数指针作为参数

例:

 

#include 

int add(int, int);
int sub(int, int);
int calc(int (*)(int, int), int, int);

int add(int num1, int num2)
{
	return num1 + num2;
}

int sub(int num1, int num2)
{
	return num1 - num2;
}

int calc(int (*fp)(int, int), int num1, int num2)
{
	return (*fp)(num1, num2);
}

int main()
{
	printf("3 + 5 = %d\n",calc(add, 3, 5));
	printf("3 - 5 = %d\n",calc(sub, 3, 5));
	
	return 0;
}

 

2.2函数指针作为返回值

例:

 

#include 

int add(int, int);
int sub(int, int);
int calc(int (*fp)(int, int), int, int);
int (*select(char ))(int, int);//int (*)(int, int);

int add(int num1, int num2)
{
	return num1 + num2;
}

int sub(int num1, int num2)
{
	return num1 - num2;
}

int calc(int (*fp)(int, int), int num1, int num2)
{
	return (*fp)(num1, num2);
}

int (*select(char op))(int, int)
{
	switch(op)
	{
		case '+': return add;
		case '-': return sub;
	}
}

int main()
{
	int num1,num2;
	char op;
	int (*fp)(int, int);
	
	/*
        printf("请输入一个式子(如1+2):");
	scanf("%d%c%d", &num1, &op, &num2);
        这样写发错误,因为接收op字符串时它会把num2的值一起接收掉。
        */ 
        printf("请输入第一个计算数:");
        scanf_s("%d", &num1);
        getchar();
        printf("请输入运算符:");
        scanf_s("%c", &op);
        getchar();
        printf("请输入第二个计算数:");
        scanf_s("%d", &num2);
        getchar();
        fp = select(op);
	printf("%d %c %d = %d\n",num1, op, num2, calc(fp, num1, num2));
	
	return 0;
}

 

 

你可能感兴趣的:(C语言)