C语言-学习之路-06
指针
内存
- 内存:内部存储器,断电即丢失数据,如DDR、DDR2、DDR3、DDR4、SRAM等。
- 外存:外部存储器,断电也仍然保证存储,如硬盘、ROM、光盘、U盘等。
物理存储器与存储地址空间
- 物理存储器:实际存在的存储器芯片。例如:内存条、显卡上的RAM等。
- 存储地址空间:对存储器编码的范围。编码:对每个物理存储单元(一个字节)分配一个号码。寻址:可以根据分配的号码找到相应的存储单元,完成数据的读写。
内存地址
- 将内存抽象成一个很大的一维字符数组。
- 编码就是对内存的每一个字节分配一个32位或64位的编号(与32位或者64位处理器相关)。
- 这个内存编号我们称之为内存地址。内存中的每一个数据都会分配相应的地址:例如:char 一个字节分配一个地址空间、int 4个字节分配四个地址空间、float 8个字节分配8个地址空间等
指针和指针变量
内存中每一个字节都对应一个编号,即 ——“地址”。
如果在程序中定义了一个变量,在对程序进行编译或运行时,系统就会给这个变量分配内存单元,并确定它的内存地址。
指针的实质就是内存地址。指针就是地址,地址就是指针。
指针时内存单元的编码,指针变量是存放地址的变量。
指针变量的定义和使用
- 指针也是一种数据类型,指针变量是一个变量。
- 指针变量指向谁,就把谁的地址赋值给指针变量。
- **“ * ”**操作符操作的是指针变量指向的内存空间。
#include <stdio.h>
int main()
{
int a = 0;
char b = 100;
printf("%p,%p\n",&a,&b);
int* p; // int * 整型指针,p是变量名 可以指向一个int类型变量的地址,所以也被称为指针变量
p = &a; //将a的地址赋值给变量p,p也是一个变量,值是一个内存地址编号。
printf("%d\n",*p); //p指向的是a的地址,*p就是a的值。
char* p1 = &b;
printf("%c\n",*p1);
return 0;
}
注:& 可以取一个变量在内存中的地址。但是,不能取寄存器变量,因为寄存器变量不在内存立,而在CPU内,所以是没有地址的。
由于指针指向的是变量的地址,我们也可以通过指针间接修改变量的值!
#include <stdio.h>
int main()
{
int a = 0;
int b = 12;
int *p1 = &a;
int *p2 = &b;
*p1 = 100;
printf("%p\n",p1);
printf("%d\n", *p1);
printf("%d\n", a);
return 0;
}
//006FFCD8
//100
//100
指针大小
可以使用sizeof()获取指针的大小,不过得到的是4(32位平台)或8(64位平台)。
printf("%d\n", sizeof(char *));
printf("%d\n", sizeof(int *));
printf("%d\n", sizeof(double *));
野指针和空指针
指针变量也是变量,是变量就可以任意赋值,不要越界即可。但是,任意数值赋值给指针变量是没有意义的,因为这样的指针就成了野指针,此指针指向的区域是未知的(操作系统不允许操作此指针指向的内存区域)。所以,野指针不会直接引发错误,操作野指针指向的内存是错误的。
#include <stdio.h>
int main()
{
int a = 100;
int *p;
p = a; //把a的值赋值给指针变量p,p为野指针,是没有意义的。
p = 0x12345678; //把地址赋值给指针变量p,p为野指针,也是无意义的。
*p = 1000; //错误操作。
return 0;
}
那么我们就不能定义一个没有指向任何变量的指针变量吗?答案是:可以的,空指针。
int main()
{
int * p = NULL;
return 0;
}
万能指针 void *
void * 指针可以指向任意变量的内存空间:
#include <stdio.h>
int main()
{
void * p = NULL;
int a = 10;
p = (void*)&a;
*((int*)p) = 11;
printf("%d\n", *((int*)p));
return 0;
}
const修饰的指针
指针常量/常量指针
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a = 10;
int b = 20;
const int c = 100; //常量
printf("%d\n", c);
//int * p = &c; // 此方式,仍然可以修改常量c的值,是不安全的,不建议使用!
//*p = 101;
//printf("&d\n", *p);
//常量指针
//修饰 *,指针指向内存区域不能修改,指针指向可以改变
const int* p1;
p1 = &a;
printf("%d\n", *p1); //10
p1 = &b;
printf("%d\n", *p1); //20
//指针常量
//修饰p2,指针指向不能变,指针指向的内存可以修改
int* const p2 = &a;
//p2 = &b;
*p2 = b;
printf("%d\n", *p2); //20
return 0;
}
指针和数组
数组名
数组名字是数组的首元素地址,但它是一个常量:
#include <stdio.h>
int main()
{
int a[] = {1,2,3,4,5,6};
printf("%p\n", a);
printf("&a[0]=%p\n", &a[0]);
return 0;
}
指针法操作数组元素
(1)加法运算
- 指针计算不是简单的整数相加
- 如果是一个int * ,+1的结果是增加一个int的大小
- 如果是一个char *, +1的结果是增加一个char的大小
#include <stdio.h>
int main()
{
int a = 0;
int* p = &a;
printf("%d\n", p);
p = p + 1;
printf("%d\n",p);
return 0;
}
//7600584
//7600588
通过改变指针指向操作数组元素:
#include <stdio.h>
int main()
{
int a[] = {0,1,2,3,4,5};
int i = 0;
int n = sizeof(a) / sizeof(a[0]);
int* p = a;
for (i = 0; i < n; i++)
{
printf("%d\n", *p);
p++;
}
return 0;
}
(2)减法运算
#include <stdio.h>
int main()
{
int a[] = {0,1,2,3,4,5};
int i = 0;
int n = sizeof(a) / sizeof(a[0]);
int* p = a+n-1;
for (i = 0; i < n; i++)
{
printf("%d\n", *p);
p--;
}
return 0;
}
#include <stdio.h>
int main()
{
int a[] = {0,1,2,3,4,5};
int *p1 = &a[2];
int* p2 = &a[1];
int n1 = p1 - p2;
int n2 = (int)p1 - (int)p2;
printf("%d\n", n1 ); // 1
printf("%d\n", n2); // 4
return 0;
}
指针数组
指针数组,它是数组,数组的每个元素都是指针类型。
#include <stdio.h>
int main()
{
//指针数组
int* p[3];
int a = 1;
int b = 2;
int c = 3;
int i = 0;
p[0] = &a;
p[1] = &b;
p[2] = &c;
for (i = 0; i < 3; i++)
{
printf("%d\n", *(p[i]));
}
return 0;
}
//1
//2
//3
指针冒泡排序:
多级指针
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void bubble(int *p,int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
//*(p+j) = p[j]
if (p[j]>p[j+1])
{
int temp = p[j];
p[j] = p[j+1];
p[j+1] = temp;
}
}
}
}
int main()
{
int arr[10] = { 4,9,10,3,5,7,1,8,2,6 };
int* p = arr;
int len = sizeof(arr)/sizeof(arr[0]);
printf("%d\n",sizeof(arr));
printf("%d\n", sizeof(p));
bubble(arr, 10);
for (int i = 0; i < len; i++)
{
printf("%d\n", arr[i]);
}
system("pause");
return 0;
}
- C语言允许有多级指针存在,在实际的程序中一级指针最常用,其次是二级指针。
- 二级指针就是指向一个一级指针变量地址的指针。
- 三级指针基本用不着,但是考试会考。
#include <stdio.h>
int main()
{
//多级指针
int a = 10;
int* p = &a; //一级指针
*p = 100; //*p 就是a ,通过指针改变a的内容。
int** q = &p; // *q = p **q = a
int*** t = &q; //*t = q **t = p ***t = a
return 0;
}
指针和函数
函数形参改变实参的值
#include <stdio.h>
void swap01(int x, int y)
{
int temp;
temp = x;
x = y;
y = temp;
printf("x=%d,y=%d\n",x,y);
}
void swap02(int* x, int* y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int a = 3;
int b = 5;
swap01(a,b); //值传递
printf("a=%d,b=%d\n",a,b);
a = 3;
b = 5;
swap02(&a, &b); //地址传递
printf("a2=%d,b2=%d\n",a,b);
return 0;
}
数组名作函数参数
数组名作函数参数,函数的形参会退化为指针:
#include <stdio.h>
void printArray(int* a, int n)
{
int i = 0;
for (i = 0; i < n; i++)
{
printf("%d\n",a[i]);
}
}
int main()
{
int a[] = { 1,2,3,4,5,6 };
int n = sizeof(a) / sizeof(a[0]);
printArray(a, n);
return 0;
}
指针作为函数的返回值
#include <stdio.h>
int a = 10;
int* getA()
{
return &a;
}
int main()
{
*(getA()) = 111;
printf("a=%d\n",a);
return 0;
}
指针和字符串
字符指针
#include <stdio.h>
int main()
{
char str[] = "hello world";
char* p = str;
*p = 'm';
p++;
*p = 'i';
printf("%s\n", str);
const char *str1 = "mike jiang";
printf("%s\n",str1);
return 0;
}
实践中常用的字符串应用模型
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//(1)利用strstr标准库函数找出一个字符串中substr出现的个数
//a)while模型
int main()
{
const char* p = "11abcd111122abcd333abcd22abcd234234qqq";
int n = 0;
//strstr() 为C语言标准库函数,它的函数原型为: char *strstr(const char *str1,const char *str2)
//其中str1为要在其中查找的字符串,str2为要查找的字符串。
//如果str2是str1的子字符串,则函数返回str2在str1中第一次出现的位置的指针。
//否则,函数返回NULL。
while ((p = strstr(p, "abcd")) != NULL)
{
//检查到存在
//重新设置起点位置
p = p + strlen("abcd");
n++;
if (*p == 0) //如果检查到0,则到结束符
{
break;
}
}
printf("n=%d\n", n);
return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//(1)利用strstr标准库函数找出一个字符串中substr出现的个数
//a)while模型
int main()
{
const char* p = "11abcd111122abcd333abcd22abcd234234qqq";
int n = 0;
do
{
p = strstr(p, "abcd");
if (p != NULL)
{
n++;
p = p + strlen("abcd");
}
else
{
break;
}
} while (*p != 0);
printf("n=%d\n", n);
return 0;
}