C语言指针详解(1)

目录

一、什么是指针

1.1、定义

 1.2、取地址操作符(&)

 1.3、指针变量和解引用操作符(*)

二、指针变量类型的意义 

 三、const修饰指针

3.1、const修饰变量

3.2、const修饰指针变量

3.2.1、const放在*的左边 

3.2.2、 const放在*的右边

 四、指针运算

4.1、指针+-整数

4.2、指针-指针

4.3、指针的关系运算 

五、野指针 

5.1、定义

5.2、成因

5.2.1、指针未初始化

 5.2.2、指针越界访问

5.2.3、 指针操作超过所指向变量的生存期

六、传值调用和传址调用


一、什么是指针

1.1、定义

指针在C语言中也叫内存地址。那么什么是内存地址呢?以生活中的房间号为例,正因为生活中的这些房间号,我们才能快速找到要找的房间。而内存地址就相当于这些房间号,有了这些内存地址,CPU就能快速找到一个内存空间。

 1.2、取地址操作符(&)

在C语⾔中,我们想要取出一个变量的地址,就要使用一种操作符:&。例如:

#include <stdio.h>
int main()
{
	int a = 10;
	printf("%p\n", &a);
	return 0;
}

上述,我们先定义了一个整型变量a,并赋值为10,然后我们打印地址。 

注:打印地址时,用%p。

结果如下:

 1.3、指针变量和解引用操作符(*)

既然我们可以用&拿出一个地址,那么是否可以用什么东西来存储地址呢?答案是有的,这种东西就叫做指针变量。如下:

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

既然我们将地址保存了起来,那么未来一定是要使用的。但是我们要怎样才能使用呢?这个时候就要用到另一种操作符:“*”(解引用操作符)。

#include <stdio.h>
int main()
{
	int a = 100;
	int* pa = &a;
	*pa = 10;
	printf("*pa=%d\n", *pa);
	printf("a=%d\n", a);
	return 0;
}

结果如下:

可以看到,通过解引用操作符,我们将*pa赋值为10,而a最终的结果也是10,由此我们可以知道:可以通过改变指针变量的值,间接改变指针变量所指向的变量的值。 

二、指针变量类型的意义 

请看如下代码,观察其运行结果:

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

 其结果如下:

我们可以看出,char*类型的指针+1后只跳过一个字节,而int*类型的指针+1后跳过了4个字节。由此,我们可以知道:不同的指针类型,加上或减去同一个数后,跳过的字节数不同。 

 三、const修饰指针

3.1、const修饰变量

变量是可以修改的,但是如果在变量前加上const,则变量便不能被修改。

3.2、const修饰指针变量

⼀般来讲const修饰指针变量,可以放在*的左边,也可以放在*的右边,意义是不⼀样的。

3.2.1、const放在*的左边 
#include <stdio.h>
int main()
{
	int m = 10;
	int n = 20;
	int const* pa = &m;
	*pa = 20;//不可行,报错
	pa = &n;//可行
	return 0;
}

上述代码说明:const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。 

3.2.2、 const放在*的右边
#include <stdio.h>
int main()
{
	int m = 10;
	int n = 20;
	int* const pa = &m;
	*pa = 20;//可行
	pa = &n;//不可行,报错
	return 0;
}

上述代码说明:const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。

 四、指针运算

4.1、指针+-整数

因为数组在内存中是连续存放的,所以只要知道第⼀个元素的地址,利用指针加减整数就能找到数组的所有元素。

#include <stdio.h>
int main()
{
	int arr[6] = { 1,2,3,4,5,6 };
	int* pa = &arr[0];
	int sz = sizeof(arr) / sizeof(arr[0]);//通过sizeof获取数组长度
	for (int i = 0; i < sz; i++) {
		printf("%d ", *(pa + i));
	}
	return 0;
}

 结果如下:

4.2、指针-指针

先说结论:指针-指针的绝对值是两个指针之间元素的个数。

由此,我们可以自行写出一个计算字符串长度的函数my_strlen:

#include <stdio.h>
size_t my_strlen(char* ch) {
	char* strat = ch;
	char* end = ch;
	while (*end != '\0') {
		end++;
	}
	return end - strat;
}
int main()
{
	char arr[] = "abcdef";
	int num = my_strlen(arr);
	printf("%d\n", num);
	return 0;
}

结果如下:

4.3、指针的关系运算 

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

 结果如下:

五、野指针 

5.1、定义

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

5.2、成因

5.2.1、指针未初始化
#include <stdio.h>
int main()
{
	int* p;//指针未初始化,默认为随机值
	*p = 10;
	return 0;
}

 规避方法:初始化指针:如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL。 NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。

 5.2.2、指针越界访问
#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i <= 11; i++){//指针越界访问
		*(p++) = i;
	}
	return 0;
}

当指针指向的范围超出数组arr的范围时,p就是野指针。

5.2.3、 指针操作超过所指向变量的生存期

当指针执行的变量的声明周期已经结束时,如果指针仍然指向这块空间,就会使该指针成为野指针。

六、传值调用和传址调用

假如叫你写⼀个函数,交换两个整型变量的值,你可能会写出如下代码:

#include <stdio.h>
void swap(int x, int y) {
	int c = x;
	x = y;
	y = c;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(a, b);
	printf("a=%d\n", a);
	printf("b=%d\n", b);
	return 0;
}

结果如下:

然后你震惊地发现,a和b的值并没有互换,这是因为a、b和x、y的地址不同,所以虽然x和y的值互换了,但是并不影响a和b的值。这就是传值调用。而传址调用则不同:

#include <stdio.h>
void swap(int* x, int* y) {
	int c = *x;
	*x = *y;
	*y = c;
}
int main()
{
	int a = 10;
	int b = 20;
	swap(&a, &b);
	printf("a=%d\n", a);
	printf("b=%d\n", b);
	return 0;
}

 结果如下:

我们发现,这次a和b的值成功互换了,这是因为这次将a和b的地址传入了函数,函数通过地址间接操作main中的a和b,这就是传址调用。

未完待续...... 

  • 21
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值