C (第十章 指针)

每一个你讨厌的现在, 都有一个不够努力的曾经!

10. 指针 (Pointer)

10.1 指针的定义
  1. 指针就是地址;
  2. 指针就是变量, 用来存放地址的变量 (存放在指针中的值都被当成地址来处理);
  3. 指针是编程语言中的一个对象, 利用地址, 它的值直接指向存在电脑存储器中另一个地方的值, 由于通过地址可以找到所需的变量单元, 可以说, 地址指向该变量单元, 因此将地址形象化地称为"指针", 意思是通过它能找到以它为地址的内存单元.
#include <stdio.h>
int main(){
	int x = 10;
	int* p = &x;
	double y = 10.0;
	double* p2 = &y;
	printf("%d\n",sizeof(p));
	printf("%d\n",*p);
	printf("%p\n",p);
	printf("%p\n",p);
	printf("%f\n",*p2);
	printf("%d\n",sizeof(p2));
	system("pause");
	return 0;
}
10.2 指针存在的原因
  1. 指针是用来存放地址的, 地址是唯一标识一块地址空间的;
  2. 指针让地址有地方存放, 指针让内存的访问更加方便;
  3. 指针的大小在32位平台下是占4个字节, 在64位平台下是占8个字节.
10.3 指针和指针的类型
10.3.1 指针的定义方式
type * 变量名
#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;	// 在内存中开辟一块空间
	printf("%p\n", p);	// 取出变量a的地址赋值给指针p, p也是一个指针变量
	return 0;
}
10.3.2 指针的类型
  • 指针的类型决定了指针向前或者向后走一步有多大 (距离).
10.4 指针的解引用
  • 指针的类型, 决定了指针解引用的时候有多大的权限 (能操作几个字节), 比如:char* 的指针解引用就只能访问一个字节, 而int* 的指针解引用就可以访问四个字节.
10.5 指针的运算
10.5.1 指针 + - 整数
  • 指针的类型决定了向前或者向后走一步有多大.
#include <stdio.h>
int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;
	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc+1);
	printf("%p\n", pi);
	printf("%p\n", pi+1);
	printf
	return 0;
}
10.5.2 指针 - 指针
#include <stdio.h>
int main(){
	char*p1 = 0x100;
	char*p2 = 0x200;
	printf("%x\n",p2 - p1);
	//指针相减,通常情况下是没有意义的,变量在哪个地址取决于操作系统,
	//再去做差,可能得到的值就会发生变化
	//指针相减其实就是求两个指针之间隔了多少个元素

	short*p3 = 0x100;
	short*p4 = 0x200;
	printf("%x\n", p4 - p3);
	system("pause");
	return 0;
}
  • 指针相减, 通常是没有意义的, 变量在哪个地址取决于操作系统, 再去作差, 可能得到的数值就会发生变化.
  • 如果两个指针指向的是同一个连续的内存空间 (数组) , 指针相减其实就是求两个指针之间隔了多少个元素.
10.5.3 指针的关系运算
  • 标准规定: 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较, 但是不允许与指向第一个元素之间的那个内存位置的指针进行比较.
int main(){
	int n1 = 10;
	int*p1 = &n1;
	int n2 = 10;
	int*p2 = &n2;
	if (p1 == p2){
		printf("呵呵\n");
	}
	else{
		printf("哈哈\n");
	}
	system("pause");
	return 0;
}
10.6 二级指针
#include <stdio.h>
int main(){
	int arr[4] = { 1, 2, 3, 4 };
	//arr是一个指向首元素的指针
	//&arr是一个指向整个数组的指针(数组指针)
    printf("%p\n", &arr[0]);//打印://0073FA7C
	printf("%p\n", arr);          //0073FA7C
	printf("%p\n", arr + 1);      //0073FA80
	printf("%p\n", &arr);	      //0073FA7C
	printf("%p\n", &arr + 1);     //0073FA8C
	system("pause");
	return 0;
}
10.7 指针和数组
10.7.1 指针数组
  1. 是数组, 一个存放指针的数组;
  2. int* arr[10];
  3. 数组名表示的是数组首元素的地址.
int* arr1[10];	// 整形指针的数组
char* arr2[4];	// 一级字符指针的数组
char* arr3[5];	// 二级字符指针的数组
int main(){
	int arr[] = { 0 };
	printf("%d\n",sizeof(arr));
	printf("%d\n", sizeof(arr+0));
	printf("%d\n", sizeof(arr[0]));
	system("pause");
	return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(){
	char w = 'w';
	char* p = &w;
	char* p2 = "hehehehe";
	char str[] = { 'a', 'b', 'c' };
	char *p3 = str;
	printf("%c\n",*p2);
	printf("%s\n", p2);
	printf("%s\n", p3);
	printf("%s\n", p2+1);
	system("pause");
	return 0;
}
//程序运行结果:
h
hehehehe
abc烫烫烫烫汤X ?
ehehehe
10.7.2 数组指针
  1. 是一个指针;
// p先和*结合,说明p是一个指针变量;
// 指向一个大小为10个整形的数组,所以p是一个指针,指向一个数组,叫数组指针。
// []的优先级是要高于*符号的,所以必须加上()来保证p先和*结合。
int (*p2)[10];

数组指针的使用

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
	int i = 0;
	for(i = 0; i < row;i++)
	{
		for(int j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

void print_arr2(int (*arr)[5], int row, int col)
{
	int i = 0;
		for(i = 0; i < row;i++)
	{
		for(int j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
	// 数组名arr表示二维数组第一行的地址,相当于一维数组的地址
	print_arr1(arr, 3, 5);
	// 使用数组指针来接收
	print_arr2(arr, 3, 5);
	return 0;
}
10.7.3 &数组名、数组名
int main(){
	int arr[4] = { 0 };
	int* p = arr;
	printf("%p\n",&arr[0]);
	//普通指针
	printf("%p\n",arr);
	printf("%p\n", arr+1);
	//数组指针
	printf("%p\n",&arr);
	printf("%p\n", &arr+1);
	//二级指针
	printf("%p\n",&p);
	system("pause");
	return 0;
}
//程序运行结果:
00B7FB30
00B7FB30
00B7FB34
00B7FB30
00B7FB40
00B7FB24
  • arr是数组名,表示的是数组首元素的地址。
  • &arr表示的是整个数组的地址,而不是数组首元素的地址。
  • &arr+1 相当于跳过整个数组。
10.7.4 数组传参

一维数组传参

  • C语言中,当一维数组做函数参数时,编译器总是把它解析成一个指向其首元素的指针。
  • 实际传递的数组大小与函数形参指定的数组大小没有关系。

二位数组传参
一级指针传参

#include <stdio.h>
void print(int* p, int sz)
{
	int i = 0;
	for(int i = 0; i < sz; i++)
	{
		printf(%d\n”, *(p + i));
	}
}

int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};
	int* p = arr;
	int sz = sizeof(arr)/sizeof(arr[0]);
	print(p, sz);
	return 0;
}

二级指针传参

#incldue <stdio.h>
void test(int** ptr)
{
	printf("num = %d\n", **ptr);
}

int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	test(&p);
	return 0;
}
10.7.4 函数指针

定义: 如果在程序中定义一个函数, 在编译时, 编译系统为函数代码分配一段存储空间, 这段存储空间的其实地址 (又称入口地址) 称为这个函数的指针.

函数指针的用途:转移表
//exp:计算器
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int Add(int x, int y){
	return x + y;
}
int Sub(int x, int y){
	return x - y;
}
int Mul(int x, int y){
	return x * y;
}
int Div(int x, int y){
	return x / y;
}
typedef int (*Func)(int x, int y);
int main(){
	Func arr[] = { Add, Sub, Mul, Div };
	while (1){
		printf("1.相加\n");
		printf("2.相减\n");
		printf("3.相乘\n");
		printf("4.相除\n");
		printf("请输入您要进行的运算: ");
		int choice = 0;
		scanf("%d",&choice);
		//转移表,表驱动的方式
		arr[choice - 1](10, 20);
	}
	system("pause");
	return 0;
}
10.7.5 函数指针数组
10.7.6 指针和数组的区别
  1. 数组是自带内存空间的, 往往把原来的数据又复制一份存放到当前新的内存空间中;
  2. 指针是不自带内存空间的, 自身内存空间就是固定的4个字节, 不管指向的是什么内容, 这4个字节只是保存到对应内存空间的地址, 不涉及数据拷贝的过程.
10.8 指针和数组面试题
//一维数组
//1.
#include <stdio.h>
#include <stdlib.h>
int main(){
	int a[] = { 1, 2, 3, 4 };
	printf("%d\n", sizeof(a));         //16  整个数组占有字节的内存
	printf("%d\n", sizeof(a + 0));     //4   隐式转换为指针
	printf("%d\n", sizeof(*a));        //4   指向首元素    
	printf("%d\n", sizeof(a + 1));     //4   指针
	printf("%d\n", sizeof(a[1]));      //4
	printf("%d\n", sizeof(&a));        //4    数组指针
	printf("%d\n", sizeof(*&a));       //16   整个数组
	printf("%d\n", sizeof(&a + 1));    //4    数组指针
	printf("%d\n", sizeof(&a[0]));     //4    首元素指针
	printf("%d\n", sizeof(&a[0] + 1)); //4    第二个元素指针
	printf("%d\n", sizeof(&*a));       //4    首元素地址
	system("pause");
	return 0;
}
总结:
1.只要是指针,sizeof()就是4个字节
2.&a数组指针,数组指针解引用到了整个数组


//字符数组
//2.
#include <stdio.h>
#include <stdlib.h>
int main(){
	char a[] = { 'a', 'b', 'c', 'd', 'e', 'f' };   
	printf("%d\n", sizeof(a));         //6   不包括'\0'
	printf("%d\n", sizeof(a + 0));     //4   指向首元素地址
	printf("%d\n", sizeof(*a));        //1   指向首元素'a'    
	printf("%d\n", sizeof(a + 1));     //4   
	printf("%d\n", sizeof(a[1]));      //1    指向元素'b' 
	printf("%d\n", sizeof(&a));        //4    数组指针
	printf("%d\n", sizeof(*&a));       //6    
	printf("%d\n", sizeof(&a + 1));    //4    数组指针
	printf("%d\n", sizeof(&a[0]));     //4    首元素指针
	printf("%d\n", sizeof(&a[0] + 1)); //4    第二个元素指针
	printf("%d\n", sizeof(&*a));       //4    首元素地址
	//未定义行为
	printf("%d\n",strlen(a));
	printf("%d\n", strlen(a+0));
	printf("%d\n", strlen(*a));
	printf("%d\n", strlen(a[1]));
	printf("%d\n", strlen(&a));
	printf("%d\n", strlen(&a + 1));
	printf("%d\n", strlen(&a[0]+1));
	system("pause");
	return 0;
}

//3.
#include <stdio.h>
#include <stdlib.h>
int main(){
	char a[] = "abcdef";
	printf("%d\n", sizeof(a));           //7    包括'\0'
	printf("%d\n", sizeof(a + 0));       //4    指向首元素的指针
	printf("%d\n", sizeof(*a));          //1    sizeof("a")    
	printf("%d\n", sizeof(a + 1));       //4   
	printf("%d\n", sizeof(a[1]));        //1    sizeof("b")   
	printf("%d\n", sizeof(&a));          //4    数组指针
	printf("%d\n", sizeof(*&a));         //6   
	printf("%d\n", sizeof(&*a));         //4    首元素地址
	printf("%d\n", sizeof(&a + 1));      //4    数组指针
	printf("%d\n", sizeof(&a[0]));       //4    首元素指针
	printf("%d\n", sizeof(&a[0] + 1));   //4    第二个元素地址

	printf("%d\n", strlen(a));           //6
	printf("%d\n", strlen(a + 0));       //6
	printf("%d\n", strlen(*a));          //未定义行为 首元素'a'=97
	printf("%d\n", strlen(a[1]));        //未定义行为
	printf("%d\n", strlen(&a));          //6
	printf("%d\n", strlen(&a + 1));      //未定义行为 越界
	printf("%d\n", strlen(&a[0] + 1));   //5
	system("pause");
	return 0;
}

//4.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
	char *p = "abcdef";
	printf("%d\n", sizeof(p));           //4    指针
	printf("%d\n", sizeof(p + 0));       //4    指向首元素的指针
	printf("%d\n", sizeof(*p));          //1    sizeof("a")    
	printf("%d\n", sizeof(p + 1));       //4   
	printf("%d\n", sizeof(p[1]));        //1    sizeof("b")   
	printf("%d\n", sizeof(&p));          //4    二级指针
	printf("%d\n", sizeof(*&p));         //4   
	printf("%d\n", sizeof(&*p));         //4    
	printf("%d\n", sizeof(&p + 1));      //4    
	printf("%d\n", sizeof(&p[0]));       //4    
	printf("%d\n", sizeof(&p[0] + 1));   //4    第二个元素指针

	printf("%d\n", strlen(p));           //6
	printf("%d\n", strlen(p + 0));       //5    从b开始,遇到'\0'结束
	printf("%d\n", strlen(*p));          //未定义行为 首元素'a'=97
	printf("%d\n", strlen(p[0]));        //未定义行为  a
	//未定义行为  &p二级指针,对应的内存中计提有多少字节还不确定
	printf("%d\n", strlen(&p));         
	printf("%d\n", strlen(&p + 1));      //未定义行为 越界
	printf("%d\n", strlen(&p[0] + 1));   //5    从下一个元素地址开始
	system("pause");
	return 0;
}

//二维数组
#include <stdio.h>
#include <stdlib.h>
int main(){
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));           //48    4*12
	printf("%d\n", sizeof(a[0][0]));     //4     
	printf("%d\n", sizeof(a[0]));        //16    4*4 a[0]=>int[4]   
	printf("%d\n", sizeof(a[0]+ 1));     //4     指向第二个元素的指针
	printf("%d\n", sizeof(*a[0]+1));     //4      =>sizeof(a[0][1])
	printf("%d\n", sizeof(a+1));         //4      数组指针
	printf("%d\n", sizeof(*(a + 1)));    //16     *(a + 1)=>a[1]
	printf("%d\n", sizeof(&a[0]+1));     //4      数组指针
	printf("%d\n", sizeof(&*a));         //4    
	printf("%d\n", sizeof(&a + 1));      //4 
	//a[0]长度为4个元素的数组,&a[0]得到了一个数组指针,&a[0]+1还是
	//数组指针,*(&a[0]+1)便得到了一各数组
	printf("%d\n", sizeof(*(&a[0]+1)));  //16     
	printf("%d\n", sizeof(*a));          //16   
	printf("%d\n", sizeof(a[3]));        //16    
	system("pause");
	return 0;
}

//num1
#include <stdio.h>
int main(){
int a[5] = {1,2,3,4,5};
int* ptr = (int*)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
system("pause");
return 0;
}
运行结果:
2,5


//num2
#include <stdio.h>
//结构体大小为20个字节
struct test{
	int num;
	char* pcname;
	char cha[2];
	short sba[4];
}*p;                           //结构体指针(未初始化,其实默认为0)
int main(){
	//%p十六进制打印
	printf("%d\n", p + 0x1);
	printf("%d\n", (unsigned long)p + 0x1);
	printf("%d\n", (unsigned int*)p + 0x1);
	system("pause");
	return 0;
}
运行结果:
0x14   0x1  0x4


//num3
int main(){
	int a[4] = {1,2,3,4};          总结://1.(int)a+1,其实地址+1
	int* ptr1 = (int*)(&a+1);           //2.对int*解引用的含义
	int* ptr2 = (int*)((int)a+1);       //3.整数在内存中如何存储
	printf("%x,%x",ptr1[-1],*ptr2);
	system("pause");
	return 0;
}
运行结果:
4, 2000000


//num4
int main(int argc,char* argv[]){
	int a[3][2] = {(0,1),(2,3),(4,5)};
	int*p;
	p = a[0];
	printf("%d\n", p[0]);              //a[0][0]=1
	system("pause");
	return 0;
}

//num5
int main(){
    //指针相减=>相间隔元素
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);
	system("pause");
	return 0;
}
运行结果:
FFFFFFFC, -4


//num6.
int main(){
	int a[2][5] = {1, 2, 3, 4, 5,
	              6, 7, 8, .9, 10};
	int *ptr1 = (int*)(&a + 1);          //&a+1会跳出整个二维数组
	int *ptr2 = (int *)(*(a + 1));       //*(a+1)=>a[1]
	printf("%d,%d",*(ptr1-1),*(ptr2-1)); 
	system("pause");
	return 0;
}
运行结果:
10,5


//num7
int main(){
	char* a[] = {"work","at","alibaba"};
	char** pa = a;                         //pa二级指针
	pa++;
	printf("%s\n",*pa);
	system("pause");
	return 0;
}
运行结果:
at


//num8.
int main(){
	char* c[] = {"ENTER","NEW","POINT","FIRST"};
	char** cp[] = {c+3,c+2,c+1};
	char*** cpp = cp;
	printf("%s\n",**++cpp);
	printf("%s\n",*--*++cpp+3);
	printf("%s\n", *cpp[-2]+3);
	printf("%s\n", cpp[-1][-1]+1);
	system("pause");
	return 0;
}
运行结果:
POINT
ER
ST
EW
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值