大家好!,前段时间一直在准备数学建模竞赛,现在也是忙完了。抓紧继续给大家带来c语言的内容。今天给大家带来指针初阶部分的讲解
当我们谈论C语言中的指针时,实际上是在讨论一种非常重要的概念,因为指针是C语言的核心之一。它们允许我们直接访问内存中的数据,这在编写高效和灵活的程序时非常有用。让我们来深入了解一下指针的概念和用法
指针理解的2个要点: 1. 指针是内存中一个最小单元的编号,也就是地址 2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量 总结:指针就是地址,口语中说的指针通常指的是指针变量
内存示意图:
指针变量: 我们可以通过 & (取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量
#include<stdio.h>
int main()
{
int a = 7;//创建变量时,在内存里开辟的一篇空间
int* pa = &a;//这里我们对变量a,取出它的地址,可以使用&操作符
//a变量占用4个字节的空间,这里是将a的4个字节的第一个字节的地址存放在p变量
//中,p就是一个之指针变量
return 0;
}
总结:指针变量,用来存放地址的变量。(存放在指针中的值都被当成地址处理)
在C语言中,指针的大小是根据计算机体系结构和操作系统的不同而变化的。通常情况下,指针的大小与计算机的位数相关。在32位系统中,指针的大小通常是4字节(32位),而在64位系统中,指针的大小通常是8字节(64位)
变量有不同的类型,整形,浮点型等。那指针有没有类型呢?
指针确实有:
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
这里可以看到,指针的定义方式是: type + * 。 其实: char* 类型的指针是为了存放 char 类型变量的地址。 short* 类型的指针是为了存放 short 类型变量的地址。 int* 类型的指针是为了存放 int 类型变量的地址
接下来探求指针类型的意义
指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节) 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节
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);
return 0;
}
可以看到pc与pi的数值均是相等的,当运行完*pc=0后,只是改变了第一个字节的数值。
当运行完*p=0后,只是改变四个字节的数值
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
int main()
{
int* pa;
*pa = 10;
return 0;
}
连编译器都会阻止我们使用未初始化的指针
int main()
{
int arr[10] = { 0 };
int* p = arr;
for (int i = 0; i <= 11; i++)
{
*(p++) = i;
}
return 0;
}
当指针指向的范围超出数组arr的范围时,p就是野指针: 自然也会报错
int* createInt() {
int num = 5;
int* ptr = #
return ptr;
}
int main() {
int* ptr = createInt();
printf("%d\n", *ptr); // 输出5,但是这是不安全的操作
// 在这之后,ptr成为了野指针,因为它指向的内存空间已经被释放
printf("%d\n", *ptr); // 未定义的行为,可能输出垃圾值或导致程序崩溃
return 0;
}
在上面的示例中,函数
createInt()
创建了一个整型变量num
,并将其地址赋给指针ptr
,然后返回了该指针。在main()
函数中,我们打印了指针ptr
所指向的值,输出结果为5。但是在createInt()
函数结束后,变量num
的生命周期结束,其所占用的内存空间被释放。此时,指针ptr
仍然保留着之前的内存地址,成为了野指针。在后续的打印操作中,我们试图访问野指针ptr
所指向的值,这是不安全的操作,可能导致程序出现未定义的行为
int
类型的变量,那么对该指针进行加1的操作,将使指针指向下一个int
类型的变量。同样地,对指针进行减1的操作,将使指针指向前一个int
类型的变量 #include<stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5 };
int* pa = arr;
for (int i = 0; i < 5; i++)
{
printf("%d ", *(pa++));
}
return 0;
}
指针-指针操作是指对两个指针进行减法运算,得到它们之间的偏移量。这种操作通常用于计算两个指针之间的距离或元素的个数
接下来我们可以利用这一点来模拟实现strlen()函数
int my_strlen(char* pa)
{
char* start = pa;
while (*pa)
{
pa++;
}
return pa - start;
}
int main()
{
char arr[] = "hello";
printf("%d", my_strlen(arr));
return 0;
}
==
:判断两个指针是否相等。如果两个指针指向同一个内存地址,则它们相等,返回1
;否则返回0
。!=
:判断两个指针是否不相等。如果两个指针指向不同的内存地址,则它们不相等,返回1
;否则返回0
。>
:判断一个指针是否大于另一个指针。指针的大小是根据指针所指向的内存地址来比较的。如果第一个指针指向的地址在第二个指针指向的地址之后,则第一个指针大于第二个指针,返回1
;否则返回0
。<
:判断一个指针是否小于另一个指针。指针的大小是根据指针所指向的内存地址来比较的。如果第一个指针指向的地址在第二个指针指向的地址之前,则第一个指针小于第二个指针,返回1
;否则返回0
。>=
:判断一个指针是否大于等于另一个指针。如果第一个指针大于第二个指针或两个指针相等,则返回1
;否则返回0
。<=
:判断一个指针是否小于等于另一个指针。如果第一个指针小于第二个指针或两个指针相等,则返回1
;否则返回0
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
1.指针是一个变量,用于存储内存地址。指针变量可以指向任何类型的数据,包括数组。通过指针,我们可以间接地访问和操作内存中的数据。 2.数组就是数组,可以存放一组数,数组的大小是取决于元素的类型和个数。 3.数组的数组名是数组首元素的地址,地址是可以放在指针变量中的。通过指针可以访问一个数组的元素
int arr[5]
,arr
可以被视为指向arr[0]
的指针。因此,可以通过指针来访问数组中的元素,例如*arr
表示数组的第一个元素,*(arr + 1)
表示数组的第二个元素。arr + 1
表示数组的第二个元素的地址,arr + 2
表示数组的第三个元素的地址。这是因为数组的元素在内存中是连续存储的int main()
{
int arr[] = { 1,2 };
int* pa = arr;
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
可见数组名通常是表示数组首元素的地址,但是有两个例外: 1.sizeof(数组名),数组名单独放在sizeof内部,数组名表示整个数组,计算的是数组的大小,单位是字节。 2.&数组名,数组名表示整个数组,取出的是数组的地址,数组的地址和数组首元素的地址是一样的,但是类型和意义是不一样的
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是 二级指针
int main()
{
int a = 10;
int* pa = &a;//一级指针
int** ppa = &pa;//二级指针
printf("%d\n", a);
**ppa = 0;//通过两次解引用来改变值
printf("%d\n", a);
return 0;
}
指针数组是是数组,是存放指针的数组
可以使用指针数组,模拟一个二维数组
int main()
{
int arr1[] = { 1,2,4,6 };
int arr2[] = { 7,2,4,4 };
int arr3[] = { 1,2,9,6 };
int* p[] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
printf("%d ", p[i][j]);
}
printf("\n");
}
return 0;
}