c语言指针基础看这一篇就够了

本文详细介绍了C语言中指针的概念,包括内存地址、取地址符、解引用操作符、指针变量的使用、不同类型指针的大小、指针运算、野指针的形成及规避、数组名的特殊性、二级指针、指针数组和函数指针等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,什么是指针

              内存中每个用于 数据 存取的 基本单位 ,都被赋予一个唯一的 序号 ,称为地址。

              C语⾔中给地址起 了新的名字叫:指针。 我们可以理解为: 内存单元的编号 == 地址 == 指针。

二,取地址符号(&),解引⽤操作符(*)和指针变量

#include <stdio.h>
int main()
{
 int a = 10;
 int* pa=&a;//取出a的地址
 printf("%p\n", pa);
 return 0;
}

上述的代码就是创建了整型变量a,内存中 申请4个字节,⽤于存放整数10,要想得到a的地址,就需要用取地址符来得到地址,然后用指针变量(类型 * 变量名)来储存地址,再用%p打印。

把地址保存起来,未来是要使⽤的,那怎么使⽤呢? 在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。 C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。

#include <stdio.h>
int main()
{
 int a = 100;
 int* pa = &a;
 *pa = 0;
 return 0;
}

*pa 的意思就是通过pa中存放的地址,找到指向的空间, *pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0.

三,指针大小

#include <stdio.h>
//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
 printf("%zd\n", sizeof(char *));
 printf("%zd\n", sizeof(short *));
 printf("%zd\n", sizeof(int *));
 printf("%zd\n", sizeof(double *));
 return 0;
}
结论:
• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

四,指针加减整数

#include <stdio.h>
int main()
{
		int n = 10;
		char* pc = (char*)&n;
		int* pi = &n;
		printf("&n=%p\n", &n);
		printf("pc=%p\n", pc);
		printf("pc+1=%p\n", pc + 1);
		printf("pi=%p\n", pi);
		printf("pi+1=%p\n", pi + 1);
		return 0;
	}

运行结果

可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。
这就是指针变量的类型差异带来的变化。

五,指针运算

5.1指针加减整数

 #include <stdio.h>
 //指针+- 整数
int main()
 { 
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int i = 0; 
 int sz = sizeof(arr)/sizeof(arr[0]);
 for(i=0; i<sz; i++)
 {
     printf("%d ", *(p+i));//p+i 这⾥就是指针+整数
 }

return 0;
}

运行结果:

p+0得到1

p+1得到2

p+2得到3

............

 5.2指针减指针

//指针-指针
#include <stdio.h>
int my_strlen(char *s)
{
 char *p = s;
 while(*p != '\0' )
 p++;
 return p-s;
}

int main()
{
 printf("%d\n", my_strlen("abc"));
 return 0; 
}

运行结果:

 指针减指针得到的是指针之间元素的个数,因此可以用来模拟库函数strlen()

5.3指针的关系运算

//指针的关系运算
#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int i = 0;
 int sz = sizeof(arr)/sizeof(arr[0]);
 while(p<arr+sz) //指针的⼤⼩⽐较
 {
  printf("%d ", *p);
  p++;
 }
 return 0; }

运行结果:

arr是第一个元素地址,sz是数组大小为10,arr+10就是元素10后面一个元素

同时p也会不断加1,这时候p<arr+sz不成立了,就不会打印了

六,野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

6.1野指针成因

1.指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2.指针越界访问
#include <stdio.h>
int main()
{
 int arr[10] = {0};
 int *p = &arr[0];
 int i = 0;
 for(i=0; i<=11; i++)
 {
 //当指针指向的范围超出数组arr的范围时,p就是野指针
  *(p++) = i;
 }
return 0;
}
3.指针指向的空间释放
#include <stdio.h>
int* test()
{
 int n = 100;
 return &n;
}

int main()
{
 int*p = test();
 printf("%d\n", *p);
return 0;
}

6.2如何规避野指针

1.指针初始化
如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL.。NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。
#include <stdio.h>
int main()
{
 int num = 10;
 int*p1 = &num;
 int*p2 = NULL;
 
 return 0; 
}
2.小心指针越界
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是越界访问
3.及时置NULL

指针不用时及时置NULL,使用前检查有效性【if(p!=NULL){}

4.避免返回局部变量的地址

例子看6.1.3test()函数

七,关于数组名的理解

#include <stdio.h>
int main()
{
 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
 printf("&arr[0] = %p\n", &arr[0]);
 printf("arr = %p\n", arr);
 return 0; 
}

可以发现数组名就是数组⾸元素(第⼀个元素)的地址。

---------------------------------------------------------------------------------------------------------------------------------

接下来看这段代码:

#include <stdio.h>
int main()
{
 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
 printf("%d\n", sizeof(arr));
 return 0;
}

 输出的结果是:40,如果arr是数组⾸元素的地址,那输出应该的应该是4/8才对。

其实数组名就是数组⾸元素(第⼀个元素)的地址是对的,但是有两个例外:

• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,单位是字节。

• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素的地址是有区别的)。

除此之外,任何地⽅使⽤数组名,数组名都表示首元素的地址。
---------------------------------------------------------------------------------------------------------------------------------
再试⼀下这个代码:
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("&arr[0] = %p\n", &arr[0]);
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}

你会发现结果一样

那arr和&arr有什么区别?

#include <stdio.h>
int main()
{
 int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
 printf("&arr[0] = %p\n", &arr[0]);
 printf("&arr[0]+1 = %p\n", &arr[0]+1);
 printf("arr = %p\n", arr);
 printf("arr+1 = %p\n", arr+1);
 printf("&arr = %p\n", &arr);
 printf("&arr+1 = %p\n", &arr+1);

 return 0; 
}

你会发现,&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是
⾸元素的地址,+1就是跳过⼀个元素。
但是&arr 和 &arr+1相差40个字节,这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。

 八,二级指针

#include <stdio.h>
int main()
{
	int a=10;
    int* p=&a;//一级指针
    int** pa=&pa;//二级指针
}
对于⼆级指针的运算有:
*pa 通过对ppa中的地址进⾏解引⽤,这样找到的是 *pa 其实访问的就是 .
int b = 20;
*pa = &b;//等价于 pa = &b;
**pa 先通过 *pa 找到 p ,然后对 p进⾏解引⽤操作: *p ,那找到的是 a .
**pa = 30;
//等价于*pa = 30;
//等价于a = 30;

 八,指针数组

指针数组的每个元素都是⽤来存放地址(指针)的。

指针数组的每个元素是地址,⼜可以指向⼀块区域。

九,字符指针变量

int main()
{
 char ch = 'w';
 char *pc = &ch;
 *pc = 'w';
 return 0;
}
还一种使用方法:
int main()
{
 const char* pstr = "hello world";//这⾥是把⼀个字符串放到pstr指针变量⾥了吗?
 printf("%s\n", pstr);
 return 0;
}

const char* pstr = "hello bit."; 特别容易以为是把字符串 hello bit 放 到字符指针 pstr ⾥了,但是本质是把字符串 hello bit. ⾸字符的地址放到了pstr中。

十,数组指针变量

• 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。

• 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。

那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量

int arr[10] = {0}; 

int(*p)[10] = &arr;//数组指针

注意区别数组指针和指针数组

十一,函数指针变量

#include <stdio.h>
int Add(int x, int y)
{
 return x+y;
}

int main()
{
 int(*pf3)(int, int) = Add;
 
 printf("%d\n", (*pf3)(2, 3));
 printf("%d\n", pf3(3, 5));

 return 0; 
}

结果是 5  8。

十二,函数指针数组

int (*par[3])();

par 先和 [] 结合,说明 par是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针。 

 基础知识就到结束了,还有的期待我下次分享吧。

JFM7VX690T型SRAM型现场可编程门阵列技术手册主要介绍的是上海复旦微电子集团股份有限公司(简称复旦微电子)生产的高性能FPGA产品JFM7VX690T。该产品属于JFM7系列,具有现场可编程特性,集成了功能强大且可以灵活配置组合的可编程资源,适用于实现多种功能,如输入输出接口、通用数字逻辑、存储器、数字信号处理和时钟管理等。JFM7VX690T型FPGA适用于复杂、高速的数字逻辑电路,广泛应用于通讯、信息处理、工业控制、数据中心、仪表测量、医疗仪器、人工智能、自动驾驶等领域。 产品特点包括: 1. 可配置逻辑资源(CLB),使用LUT6结构。 2. 包含CLB模块,可用于实现常规数字逻辑和分布式RAM。 3. 含有I/O、BlockRAM、DSP、MMCM、GTH等可编程模块。 4. 提供不同的封装规格和工作温度范围的产品,便于满足不同的使用环境。 JFM7VX690T产品系列中,有多种型号可供选择。例如: - JFM7VX690T80采用FCBGA1927封装,尺寸为45x45mm,使用锡银焊球,工作温度范围为-40°C到+100°C。 - JFM7VX690T80-AS同样采用FCBGA1927封装,但工作温度范围更广,为-55°C到+125°C,同样使用锡银焊球。 - JFM7VX690T80-N采用FCBGA1927封装和铅锡焊球,工作温度范围与JFM7VX690T80-AS相同。 - JFM7VX690T36的封装规格为FCBGA1761,尺寸为42.5x42.5mm,使用锡银焊球,工作温度范围为-40°C到+100°C。 - JFM7VX690T36-AS使用锡银焊球,工作温度范围为-55°C到+125°C。 - JFM7VX690T36-N使用铅锡焊球,工作温度范围与JFM7VX690T36-AS相同。 技术手册中还包含了一系列详细的技术参数,包括极限参数、推荐工作条件、电特性参数、ESD等级、MSL等级、重量等。在产品参数章节中,还特别强调了封装类型,包括外形图和尺寸、引出端定义等。引出端定义是指对FPGA芯片上的各个引脚的功能和接线规则进行说明,这对于FPGA的正确应用和电路设计至关重要。 应用指南章节涉及了FPGA在不同应用场景下的推荐使用方法。其中差异说明部分可能涉及产品之间的性能差异;关键性能对比可能包括功耗与速度对比、上电浪涌电流测试情况说明、GTH Channel Loss性能差异说明、GTH电源性能差异说明等。此外,手册可能还提供了其他推荐应用方案,例如不使用的BANK接法推荐、CCLK信号PCB布线推荐、JTAG级联PCB布线推荐、系统工作的复位方案推荐等,这些内容对于提高系统性能和稳定性有着重要作用。 焊接及注意事项章节则针对产品的焊接过程提供了指导,强调焊接过程中的注意事项,以确保产品在组装过程中的稳定性和可靠性。手册还明确指出,未经复旦微电子的许可,不得翻印或者复制全部或部分本资料的内容,且不承担采购方选择与使用本文描述的产品和服务的责任。 上海复旦微电子集团股份有限公司拥有相关的商标和知识产权。该公司在中国发布的技术手册,版权为上海复旦微电子集团股份有限公司所有,未经许可不得进行复制或传播。 技术手册提供了上海复旦微电子集团股份有限公司销售及服务网点的信息,方便用户在需要时能联系到相应的服务机构,获取最新信息和必要的支持。同时,用户可以访问复旦微电子的官方网站(***以获取更多产品信息和公司动态。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值