补充C语言04:指针和数组

补充C语言04:指针和数组,第1张

指针是怎么来的

  • 计算机只认识二进制,
  • 32位机器,有32根地址线,而一根地址线又用无线电信号01来表示,也就相当于开闭
  • 所谓的编程(地址)体现在地址总线的排列组合上
  • 2^32个地址*1字节 = 2^10*2^10*2^10 *2^2 = 4GB 

补充:有了指针(地址)会大大的提高效率,不存在指针(地址)的话, 只能一个一个的遍历


如何理解指针和指针变量
  1. 指针就是地址,地址也是数据
  2. 指针变量本质是变量,里面保存的是地址(指针)值
  3. 指针变量:空间(左值)+内容(右值:地址)
#define _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
	int* p = NULL;
	p = (int*)0x1234;
	int* q = p;
	return 0;
}
  • 第一个p使用的是变量p的空间
  • 第二个p使用的是变量p的内容,也就是0x1234,此时指针==指针变量!!
那么对指针变量进行解引用,使用的是左值,还是右值?
  •  通过实验发现对控制针进行解引用,会发生访问冲突,
  • 0x1234如果是赋给p指针变量的空间的话,是不会报错的,所以推断出0x1234是赋给p指针变量的内容,

结论:对指针变量进行解引用,使用的是指针变量的右值(内容)

补充: 对指针进行解引用,代表指针所指向的目标


变量的地址
#define _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
	int a = 10;
	printf("%p", &a);
	return 0;
}
  • 感兴趣的可以试试,只要不是用很老的编译器,你会发现每次打印出来的地址都不一样
  • 那个东西叫栈随机化技术,是为了提高安全性
编译器bug
#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main() 
{ 
	int* p = NULL; 
	p = (int*)&p; 
	*p = 10; 
	p = 20; 
	return 0; 
}
  • 这是一段有趣的代码,这里的p是自己指向自己
  • 我这里不好复现,感兴趣的话可以自己试试,
  • 注意:要用老的编译器

 补充:对指针加1,就是加上其指向类型的大小


数组的内存布局

  • 局部变量都是在栈区上面的,而栈区的使用习惯是先使用高地址再使用低地址
  • 而a先定义意味着,a先开辟空间,那么a就先入栈,所以a的地址最高
  • 数组是线性且连续的,所以地址也是依次递增的
  • 在开辟空间的角度,不应该把数组认为成一个个独立的元素,要整体开辟,整体释放
数组传参
#define _CRT_SECURE_NO_WARNINGS 1
#include 
void show(int pr[])
{
	int i = 0;
	for (i = 0;i < 10;i++){
		printf("%d ", *(pr + i));
		//printf("%d ",pr[i]);
	}
}
int main() 
{ 
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	show(arr);
	return 0; 
}
  •  所有的数组,传参都会发生降维,都会降维成指向内部元素类型的指针!
  • 如果不降维成指针,就会发生数组拷贝,函数的调用效率也会降低

补充:在C语言中,任何函数的调用,只要有形参实例化,就必然会形成临时拷贝!!!


指针和数组访问元素的相似性
#define _CRT_SECURE_NO_WARNINGS 1
#include 
void show(int* pr)
{
	int i = 0;
	for (i = 0;i < 10;i++){
		printf("%d ", *(pr + i));
		//printf("%d ",pr[i]);
	}
	printf("\n");
}
int main() 
{ 
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	show(arr);
	for (i = 0;i < 10;i++) {
		printf("%d ", arr[i]);
		//printf("%d ", *(arr + i));
	}
	return 0; 
}
  • 首先数组和指针肯定是不一样的,只是他们都可以用*[ ]进行访问,但是寻址方案是完全不一样的
那么C语言为什么要这样设计呢 
  • 如果数组和指针的访问元素的方式不一样,那么对于一个程序员来说,他就需要不断的在不同的代码片段处,进行习惯的切换
  • 所以干脆,C将指针和数组的访问方式打通,让程序员在函数内,也好像使用数组那样进行元素访问,本质值减少了编程难度!
奇怪的数组定义
#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	int a = 0;
	float b = 0.0f;
	int arr[10] = { 0 };
	//int [10] arr = { 0 };
	return 0;
}
  • 在C语言中像int a = 0;float b = 0.0f,都是类型在左,变量名在右,
  • 为什么不能这样定义呢,int [10] arr = { 0 };
另外的一种写法
#define _CRT_SECURE_NO_WARNINGS 1
#include 
typedef int* p1_t[10];
typedef int(*p2_t)[5];
int main()
{
	p1_t x;
	p2_t y;
	return 0;
}
数组里的元素是数组类型的一部分吗?

  •  实验表明:数组里的元素是数组类型的一部分
经典题
#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	int a[4] = { 1,2,3,4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x\n", ptr1[-1], *ptr2);
	return 0;
}

 

  •  当前大多数机器都是采用小端存储,
  • 存的时候使用小端存,那取的时候自然也要用小端取,
 多维数组如何画图

  •  所有的数组都可以当成"一维数组",多维数组就相当于一维数组不停的套娃

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/563423.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)

保存