从零开始的c语言---初步理解指针(概念详解)
前言:
距离上次更新博客已经过去了一个月,已经学了新知识,但是没有及时完成博客,这次我详细来讲述c语言的大关---------指针。
1. 内存和地址
1.1 内存
我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何⾼效的管理呢?
其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节。
计算机中常⻅的单位: ⼀个⽐特位可以存储⼀个2进制的位1或者0
中,每个内存单元,相当于⼀个学⽣宿舍,⼀个字节空间⾥⾯能放8个⽐特位,就好⽐同学们住的⼋⼈间,每个⼈是⼀个⽐特位。
1.2 地址
c
#include <stdio.h>
int main() {
int a = 10; // 变量a,存在某块内存里(比如地址是0x0012FF44)
int *p = &a; // p是指针变量,存的是a的地址(&是"取地址符")
printf("a的值:%d\n", a); // 输出:a的值:10
printf("a的地址:%p\n", &a); // 输出:a的地址:0x0012FF44(具体地址依环境而定)
printf("p存的地址:%p\n", p); // 输出:p存的地址:0x0012FF44
printf("p指向的值:%d\n", *p); // *是"解引用符",通过地址取对应的值 → 输出:10
return 0;
}
2.指针语法
2.1 指针变量的定义
格式:类型 *指针变量名
类型:指针指向的变量的类型(比如int *p表示 p 指向的是 int 类型变量);
*:表示这是一个指针变量。
cpp
// 不同类型的指针
int *p_int; // 指向int的指针
char *p_char; // 指向char的指针
float *p_float;// 指向float的指针
2.2. 两个关键操作符
取地址符 &:获取变量的内存地址,赋值给指针变量;
解引用符 *:通过指针变量存的地址,访问对应的变量的值。
代码示例(修改指针指向的值):
c
int a = 10;
int *p = &a;
*p = 20; // 通过指针修改a的值
printf("a = %d\n", a); // 输出:a = 20
指针的 "大小"
指针变量的大小,只和操作系统的 "地址位数" 有关(和指向的类型无关):
32 位系统:地址是 32 位 → 指针大小是 4 字节;
64 位系统:地址是 64 位 → 指针大小是 8 字节。
c
#include <stdio.h>
int main() {
int *p_int;
char *p_char;
printf("int指针大小:%zu字节\n", sizeof(p_int)); // 64位系统输出8
printf("char指针大小:%zu字节\n", sizeof(p_char));// 64位系统输出8
return 0;
}
2.3野指针:
1.指针未初始化:指向随机地址(可能导致程序崩溃)
c
int *p; // 未初始化的野指针
*p = 10;
解决:指针初始化时设为NULL(空指针),或直接指向有效变量。
2.指针类型不匹配:比如用char*指针指向int变量(可能导致数据截断)
c
int a = 1000;
char *p = &a;
2.4指针的基本运算
废话不多说,代码如下:
c
#include <stdio.h>
int main() {
int a = 10;
int* p = &a;
// 打印指针地址(十六进制)和指针+1后的地址
printf("p的地址:%p\n", p); // 例:0x7ffeefbff5c4
printf("p+1的地址:%p\n", p+1); // 例:0x7ffeefbff5c8(偏移4字节)
char ch = 'a';
char* pc = &ch;
printf("pc的地址:%p\n", pc); // 例:0x7ffeefbff5c3
printf("pc+1的地址:%p\n", pc+1); // 例:0x7ffeefbff5c4(偏移1字节)
return 0;
}
int类型的指针+1------------访问4个字节
char类型的指针+1------------访问1个字节
- 指针 - 指针:计算地址间的元素个数,只针对连续存放的数组,否则没有意义。


2.5const修饰指针
之前我们学过了const,但是为什么要在学一遍呢,const原本是为了变量不被修改所以才设置的,请看一下代码
c
int main()
{
const int i = 0;
printf("i=%d\n", i); //const修饰整型变量
int* p = &i;
*p = 9;
printf("i=%d\n",*p);
return 0;
}

首先我们用const修饰了整型变量,但事实上i的值还是改变了,当我们用指针访问时,i仍旧会改变。
const指针的具体用法
const修饰的是它右边最近的内容------ 这是区分所有 const 指针的核心口诀。
指针指向的内容不能被修改(const 修饰指针指向的变量)
语法:const 类型* 指针名 或 类型 const* 指针名(两种写法等价)
规则:指针变量本身可以改(指向其他地址),但通过指针不能修改指向的内容。
c
#include <stdio.h>
int main() {
int a = 10, b = 20;
const int* p = &a; // 或 int const* p = &a
// *p = 30; → 错误:不能通过p修改a的值
p = &b; // 正确:指针本身可以指向其他地址
printf("*p = %d\n", *p); // 输出:*p = 20
return 0;
}
- 指针变量本身不能被修改(const 修饰指针变量)
语法:类型* const 指针名
规则:指针变量本身不能改(不能指向其他地址),但通过指针可以修改指向的内容。
代码示例:
c
#include <stdio.h>
int main() {
int a = 10, b = 20;
int* const p = &a; // 指针p本身被const修饰
*p = 30; // 正确:可以修改a的值
// p = &b; → 错误:不能修改p的指向
printf("a = %d\n", a); // 输出:a = 30
return 0;
}
- 指针本身 + 指向的内容都不能被修改(const 同时修饰两者)
语法:const 类型* const 指针名
规则:指针既不能改指向,也不能通过指针修改内容。
c
#include <stdio.h>
int main() {
int a = 10, b = 20;
const int* const p = &a;
// *p = 30; → 错误:不能改内容
// p = &b; → 错误:不能改指向
printf("*p = %d\n", *p); // 输出:*p = 10
return 0;
}
3,传值调用和传址调用
传地址:
c
void reverse(int* x, int* y)//传入指针变量
{
int temp = 0;
temp = *x;
*x = *y;
*y = temp;//指针交换
}
int main()
{
int a = 20;
int b = 10;
printf("交换前的a=%d,b=%d\n", a, b);//20 10
reverse(&a, &b);//实参地址
printf("交换后的a=%d,b=%d\n", a, b);//10 20
return 0;
}
传值调用
c
void change(int x) {
x = 100; // 修改的是x的副本
}
int main() {
int a = 10;
change(a);
printf("a = %d\n", a); // 输出:a = 10(原变量没被修改)
return 0;
}
如图所示,传值和传地址是不同的,这是为什么呢,如果是传值,改变形参的变量,实参是不会改变的,形参只是实参的临时拷贝,所以不会影响到是实参。
传地址:通过指针的访问,传入实参的地址,通过指针间接访问实参修改了值。
4.易错点和 "坑"
(*p)++:先取解引用后的值,在加加;
*p++:先取 p 指向的值,再将 p 后移 1 位;
*++p:先将 p 后移 1 位,再取新地址的值。
越界访问:指针偏移超出数组 / 内存边界,会访问到非法内存
c
#include<stdio.h>
#include<assert.h>
#include<string.h>
int main()
{
int arr[10] = { 0 };
int* p = &arr[0];
int i = 0;
for (i = 0; i <= 11; i++)
{
*(p++)=i;//指针越界访问
}
p = NULL;
assert(p != NULL);//括号里面的是条件
return 0;
}

c
////实现strlen功能
int my_strlen(char* p)
{
int count = 0;
assert(p != NULL);
while (*p)//遍历数组的字符=*p!='\0'
{
count++;
p++;
}
return count;
}
int main()
{
char arr[] = "abcdef";
int len=my_strlen(arr);
printf("%d", len);
return 0;
}
完
不积跬步,无以至千里。