文章转载请注明出处,加上原文链接,谢谢!https://blog.csdn.net/weixin_46959681/article/details/111768618
前言
在上一篇博客中,笔者就指针的些许基础概念进行了讲解,但就指针的实现范其围远不止如此。指针可以与其他的概念进行组合,如配合数组实现指针数组、数组指针,配合函数实现指针函数、函数指针等。笔者就上述内容在接下来的篇幅中展开详细说明。
指针数组
指针数组实际上是关于指针的数组,核心为一个数组,该数组中的每一个元素都是指针,如 int *parray[N]
。
#include <stdio.h>
#define N 3
int main()
{
int i;
int array[N] = {
5, 50, 500};
//定义了一个指针数组。
int *parray[N];
//同指针声明的初始化一样,这里为指针数组的初始化。
for(i = 0; i < N; i++)
parray[i] = &array[i];
//使用for遍历指针数组。
for(i = 0; i < N; i++)
printf("%d\n", *parray[i]);
return 0;
}
运行结果:
5
50
500
数组指针
数组指针实际上关于数组的指针,核心为一个指针,该指针指向某个数据类型的数组,如 int (*p)[3]
注意⚠️:数组的指针在偏移的时候偏移的是整个数组的大小。
#include <stdio.h>
#define N 3
int main(){
int array[N] = {
1, 10, 100};
int i = 0;
// (*p)代表了数组整体。
int (*p)[N];
//取地址一维数组名才是数组指针,第一个元素地址&array[0]不行。
p = &array;
//打印数组指针中所有整型元素的地址。
for(i = 0; i < N; i++){
printf("%p\n", p[i]);
}
//打印数组指针中所有整型元素的值
for(i = 0; i < N; i++){
printf("%d\n", (*p)[i]);
}
return 0;
}
运行结果:
0x7ffea03116cc
0x7ffea03116d8
0x7ffea03116e4
1
10
100
对于数组、指针两者组合何以成为数组指针、指针数组,在符号
*
、[]
、()
三者的优先级以及使用画图阐述,这篇博客讲解的很到位,建议阅读。链接
指针函数
指针函数实际上是一个返回指针的函数, 核心是一个函数,该函数的返回值是一个指针,如 int *add(int x, int y)
。返回值是一个 int
类型的指针,是一个地址。
#include <stdio.h>
int *add(int x, int y)
{
int sum;
sum = x + y;
//定义一个指针,将其作为一个返回值。
int *p;
p = ∑
return p;
}
int main()
{
int x;
int y;
printf("Please enter two numbers:\n");
scanf("%d", &x);
scanf("%d", &y);
//调用指针函数。
int *p = add(x, y);
printf("sum = %d\n", *p);
return 0;
}
运算结果:
函数指针
与指针函数不同,函数指针的本质是一个指针变量。该指针的地址指向了一个函数,所以它是指向函数的指针。(换句话说,该指针变量存放的是函数的地址。)
#include <stdio.h>
void printwelcome()
{
printf("hello, world.\n");
}
int Add(int a, int b)
{
return a+b;
}
int main()
{
//定义一个函数指针。
int (*fun1)(int a, int b);
//两种初始化皆可。
fun1 = Add;
// fun1 = &Add;
//函数调用三者皆可以,建议使用第一种。
int ret1 = fun1(1, 2);
// int ret1 = (*Add)(1, 2);
// int ret1 = (*fun1)(1, 2);
printf("ret1 = %d\n", ret1);
void(*p)();
p = printwelcome;
//函数调用,三者都可以。
p();
// (*p)();
// (printwelcome)();
return 0;
}
运行结果:
浅说malloc
函数 malloc
的全称是 Memory allocation,原型为 void *malloc(size_t.size)
中文叫动态内存分配 ,用于申请一块连续的指定大小的内存块区域以 void*
类型返回分配的内存区域地址。当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存,且分配的大小就是程序要求的大小。无类型的指针也是一个指针变量,但是我们不知道它指向的空间是什么属性。在C与C++中规定,void*
类型可以通过类型转换成任何类型的指针,如 int *p = (int *)malloc(10*sizeof(int)+1)
。
内存泄漏有以下原因:
- malloc申请的内存空间,程序不会主动释放。(Linux会主动释放 )
- 也许程序在循环中一直申请内存空间。
- 程序访问已经被释放掉的内存空间
- 访问没有权限的内存
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n;
printf("请输入学生总人数:\n");
scanf("%d", &n);
//将开辟的无类型的指针强转成整形类的指针。
int *parray;
parray = (int *)malloc(n * sizeof(int));
int i;
for(i = 0; i < n; i++)
{
printf("请输入第%d个学生的成绩: \n", (i+1));
scanf("%d", &parray[i]);
}
for(i = 0; i < n; i++)
{
printf("第%d个学生的成绩是%d \n", (i+1), parray[i]);
}
return 0;
}
类型总结
int i; //定义一个整型变量
int *p; //定义了一个指向整型变量的指针
int array[5] = {
0}; //定义了一个含有5个整型元素的数组
int *parray[4]; //定义了一个指针数组,含有4个指向整型数据的指针元素(地址)。
int a = 1;
int b = 2;
int c = 3;
int d = 4;
//初始化指针数组
parray[0] = &a;
parray[1] = &b;
parray[2] = &c;
parray[3] = &d;
//数组指针
int array[4] = {
0};
//又或者是 int[4](*p),但前者比较美观。 (指针数组)
int (*p)[4];
p = array;
int f(); //f为返回值为整型数值的函数
int* p(); //p为一个返回值为指针的函数,该指针指向整型数据。
int (*p)(); //p为指向函数的指针,该函数返回一个整型值。
void *p(); // p是一个指针变量,基本类型为void(空类型),不指向具体的对象。
}
volatile
volatile 是类型修饰符(函数)。volatile 的作用是不再寄存器中取数据,而是从内存中获取数据,避免了多线程中可能出现的“数据更新错误的情况”。虽然牺牲了效率,但是提高了程序运行结果的准确度。
变量名前若添加了类型修饰符,则说明运算中会从内存中重新装载内容,而不是直接从寄存器中拷贝数据。典型场景有并行设备的硬件寄存器(如:状态寄存器),一个中断服务子程序中访问到非自动变量,多线程应用中被几个任务共享的变量等等。
同时,volatile 和编译器的优化有关。在某次线程内,当读取一个变量时,为了提高读取速度,编译器进行优化时有时会先把变量读取到一个寄存器中。当再次读取变量值时,就从寄存器中直接读取。当变量值在本线程里改变时,会同时把变量的新数值拷贝到寄存器中,以保持一致。当变量因别的线程值发生,寄存器中的值不会产生相应改变,从而造成相应读取的值和实际的变量值不一致。当寄存器因别的线程值发生改变,同理有原变量的值也不会相应改变,也会造成应用程序读取的值和实际的变量值不一致。
文章更新记录
- 文章脉络初步搭好。 「2020.1.1 21:06」
- 文章后半段内容整理。 「2020.1.2 10:17」