新手一看就懂——指针知识点总结

新手一看就懂——指针知识点总结

  • 指针
    • 什么是指针
    • 指针简单上手
    • 指针的分类
      • 整形指针&字符指针
      • void指针
      • 数组指针
      • 函数指针
    • 指针的类型与定义的意义

指针

什么是指针

指针是我们进阶C语言的“拦路虎”,因为他在小白看来其定义如此复杂多变,难以捉摸。可是大神或者有一定变成经验的人来看,就是一个简答的概念。我们只要记住:指针即是变量又是地址。就像光具有波粒二象性一样。你可以把指针理解成为地址(方便理解),但是其本质还是一个变量只不过这个变量是专门存放地址的。

指针简单上手

int a = 100;
	printf("%d", a);
	int* p = &a;//这个p就是存放a的地址的指针变量,任何指针变量在定一起类型的时候要加一个‘*’声明其是一个指针变量。
	*p = 20;
	printf("%d", a);//通过找到地址可以更改这的地址所指的内容。

新手一看就懂——指针知识点总结_第1张图片

指针的分类

整形指针&字符指针

这两个是比较常见和容易理解的指针,依次用int和char表示,他们的区别在于指向变量类型不同,内存也不一样,在进行解引用操作时访问的字节大小也因为变量类型的区别会有所差异。整型指针可以访问4个字节,而字符指针只能访问1个字节。也就是说对整型指针变量解引用,一次可以操作一个整型,而对字符变量解引用一次只能操作一个字符。
较为特殊的char* p="hello"这并不是将整个字符串的地址传个了p,而是传了字符穿首元素‘h’的地址,可以通过’h‘的地址来找到整个字符串。此时出现char*p2=“hello”,p2和p代表的是同一处地址,因为hello是常量字符串,没有必要开辟两块不同的空间的来存储它。这是字符指针的一个特性。

  • 整型指针 - int* - 指向整型的指针
    字符指针 - char * - 指向字符的指针
	int a = 100;
	char ch = 'm';
	char* pc = &ch;
	printf("%d\n", a);
	printf("%c\n", ch);
	int* p = &a;
	*p = 20;
	*pc = 's';
	printf("%d\n", a);
	printf("%c\n", ch);

新手一看就懂——指针知识点总结_第2张图片

void指针

void型的指针可以接受任何类型的地址,但是不能对void型指针进行解引用操作。解引用操作要有特定的访问字节的数量,比如对整型指针解引用就是访问4个字节,字符型指针解引用就是访问1个字节,而void型指针无法确定访问字节个数,所以不能进行解引用操作。同时void这种类型的指针也不能进行加减整数的操作,因为无法确定跳过的字节个数。
典型的例子可以举一个 memmove函数。它可以对任意类型的数组变量进行粘贴,且可以出现重叠。
void * memmove ( void * destination, const void * source, size_t num );
在memmove的声明中,出现了void * 这个类型的变量,memmove可以对任意类型进行处理,因为可以通过void
类型转化为想要的类型。定义子有很多,不会像strncpy只可以针对字符串。

/* memmove example */
#include 
#include 

int main ()
{
  char str[] = "memmove can be very useful......";
  memmove (str+20,str+15,11);
  puts (str);
  return 0;
}

数组指针

这是一种指向数组的指针,例如int(p)[10]这就是一个指向数组的指针,它指向的数组有10个元素,每个元素都是整型。给p加上括号是因为p和[10]优先结合,这样的话就变成了一个数组而不是指针了。这个数组叫 指针数组 ,int*p[10]这样的写法意思是一个有10个元素的数组,每一个元素都是整型指针,这和数组指针是两个不同的东西。
指向数组的指针里面存放的便是数组的地址,而非数组某个元素的地址,所以在定义数组指针时要用 &+数组名,而不是简单使用 数组名。
//关于数组名
//数组名是数组首元素的地址
//但是有2个例外:
//1. sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节
//2. &数组名 - 数组名也表示整个数组,取出的是整个数组的地址
//除了这个2个例外,你见到的所有的数组名都表示首元素的地址
数组指针:指向数组的指针

	char arr[] = "abcdef";
	char* pa = arr;
	printf("%s", pa);

新手一看就懂——指针知识点总结_第3张图片
但是我们还是要区分另外一个概念就是 指针数组它是存放指针的数组。二者不要搞混了。

	int a[] = { 1,2,3,4 };
	int* p = a;
	int (*pa)[4] = &a;//这个是 数组指针
	printf("%p\n", p);
	printf("%p\n", p+1);

	printf("%p\n", pa);
	printf("%p\n", pa+1);
	char arr[] = "abcdef";
	char arr2[] = "asdfgh";
	char arr3[] = "qwerty";
	char arr4[] = "zxcvbn";
	char* pa[4] = { arr,arr2,arr3,arr4 };//这是 指针数组(多个函数也可以这样制作一个函数指针数组)
	printf("%s\n", pa[0]);
	printf("%s\n", pa[1]);
	printf("%s\n", pa[2]);
	printf("%s\n", pa[3]);

新手一看就懂——指针知识点总结_第4张图片

函数指针

函数指针顾名思义就是指向函数的指针,每个函数都有一个入口,这个入口的地址便是函数指针所指向的地址。函数地址的表示方法为 函数名或 &+函数名。例如一个函数叫Add,&Add和Add都是表示这个函数的地址没有什么差别。函数指针的写法是 函数的返回类型()(函数的参数),例如函数Add,其函数指针的写法就是int('‘p)(int,int)=Add 。’'p要加上括号来保证和p的优先结合来形成一个指针变量,如果不加括号来优先结合,则会出现int* p(int,int)这样的写法,这就变成了函数的声明,这个函数的返回类型是int*,函数的名字叫p,函数的参数是2个整型和原先的函数指针不是同一个意思。
用函数指针调用函数时可以不加*这个解引用符号,因为这个符号将不会在程序运行的时候起到作用。

//函数指针-也是指针-指向函数的指针

int Add(int x, int y)
{
	return x + y;
}

int main()
{
	//pf就是函数指针变量
	int (*pf)(int, int) = Add;//&Add
	//int sum = (*pf)(3, 5);
	int sum = pf(3, 5);

	printf("%d\n", sum);

	return 0;
}

指针的类型与定义的意义

指针为什么要有类型呢?因为指针的类型决定了指针偏移的步长
接下来给出一些案例来做具体分析。

//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//a在sizof()内代表整个数组,要计算整个数字大小 =4*4
printf("%d\n",sizeof(a+0));//a不是单独存在,代表a数组的首元素地址——&a[0],x86地址占4个字节,x64占8字节
printf("%d\n",sizeof(*a));//a不是单独存在,代表a数组的首元素地址——&a[0],*&a[0]=a[0].代表一个整形的大小=4
printf("%d\n",sizeof(a+1));//a是首元素的地址,a+1是第二个元素的地址,sizeof(a+1)计算的是指针的大小 - 4/8(4 or 8 下同)
printf("%d\n",sizeof(a[1]));//4——a[1]就是数组的第二个元素,sizeof(a[1])的大小
printf("%d\n",sizeof(&a));//&a是取了整个数组的地址,但还是地址——4/8
printf("%d\n",sizeof(*&a));//*&a=a 还是一整个数组的大小,16
printf("%d\n",sizeof(&a+1));//&a是取了整个数组的地址,再加一是往后移动16字节(a数组的大小)的下一个地址,还是地址 4/8
printf("%d\n",sizeof(&a[0]));//第一个元素的地址,地址就是4/8
printf("%d\n",sizeof(&a[0]+1));//第二个元素的地址 4/8

新手一看就懂——指针知识点总结_第5张图片

下一题

char arr[] = {'a','b','c','d','e','f'};//字符数组
printf("%d\n", sizeof(arr));//代表整个数组的大小=1*6
printf("%d\n", sizeof(arr+0));//代表的是&arr[0]+0,还是一个地址=4/8
printf("%d\n", sizeof(*arr));//等于*&arr[0]=arr[0] 为1
printf("%d\n", sizeof(arr[1]));// 等于1
printf("%d\n", sizeof(&arr));//4/8
printf("%d\n", sizeof(&arr+1));//跳到arr【】数组之后的地址,还是地址=4/8
printf("%d\n", sizeof(&arr[0]+1));//这个是arr[1]的地址,地址就是4/8
//一定要区分sizeof和strlen,前者是运算符,而后这是函数(函数传入地址,可以进行运算)
printf("%d\n", strlen(arr));//随机数,因为虽然strlen读取字符串的大小终止的符号是‘\0’如果见不到就会一直走
printf("%d\n", strlen(arr+0));//&arr[0]+0 还是一个地址,strlen继续往后读得到的还是随机数
printf("%d\n", strlen(*arr));//*&arr[0]=arr[0]不是一个地址,无法计算,非法访问,err
printf("%d\n", strlen(arr[1]));//arr[1]不是一个地址,无法计算,非法访问,err
printf("%d\n", strlen(&arr));//随机数,同第一个一样。
printf("%d\n", strlen(&arr+1));//随机数
printf("%d\n", strlen(&arr[0]+1));//随机数

sizeof的案例结果:
新手一看就懂——指针知识点总结_第6张图片

strlen:出错的已经屏蔽掉,不然无法运行。
新手一看就懂——指针知识点总结_第7张图片

给出两个练习题,下期我公布答案并讲解。

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

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