指针与内存地址

 
内存,计算机中的内存,通常说的是虚拟内存,
它是所有硬件内存条在操作系统,并且是运行起来的OS中的一个地址映射

比如,电脑中插入4个16G的内存条,那么电脑的内存为64G

这个64G就是从0开始,然后1,2,3,...,直到64G-1 
可以将之看作一个刻度尺,0代表第1格,1代表第2格,64G-1代表最后一格

所有的进程使用的都这一个虚拟内存,毕竟整个电脑 也就这64G内存

0,1,2,3,...虚拟内存对应的数字标记,就是内存地址,也叫指针

地址3,就是虚拟内存的第4个格,
每个格是一个字节,一个Byte,代表了8位,就是8bit,
一个bit可以是0或1,
8bit表示一个字节是这样的:00000010,这个数表示2,是2进制

指针就是地址,就是一个数字,这个数字对应着虚拟内存上的一个字节位置

整数变量

数字变量

 
int a =1;
这是一个整数变量,也是一个变量,也就是说它不是常量,即a的值可以变化,比如
a =2;

a这个字符标记,对应着虚拟内存上的一个地址,就是一个数字,
&a可以取变量a的地址

C语言中进制前缀

 
二进制
前缀 0b 示例 0b100 十进制 4

八进制
前缀 0 示例 0100 十进制 64

十进制
无前缀 示例 100 十进制 100

十六进制
前缀 0x 示例 0x100 十进制 256

 
#include <stdio.h> 
int main(){
    int a=1;
    printf("%p\n",&a);//0x7ffcf4fb0a7c
    a=2;
    printf("%p\n",&a);//0x7ffcf4fb0a7c
    return 0;
}

 
变量a从1到2的过程中,其地址是不变的,
就是将虚拟内存中地址为0x7ffcf4fb0a7c为开头及之后的三个格子,一个共四个格子,存储的数字从1变更为2

一个地址只能表示一个格子,但整数int在计算机中占用4个字节,变量a的数据类型为int,就表示它要用四个格式

但&变量返回的地址,只有一个数字,是一个地址,
将前后连续的四个格子看作一个数据单元这个操作由C语言自动完成

三个角度

 
变量a 
操作一个变量,比如输出,是则是输出 变量a的值 

&a 每个变量在栈上,也只能是在栈上,分配有一个地址,&a指取变量a在栈上的地址 

*(&a) 即*号可以从一个地址中取出该地址对应的值 
    
指针与指针变量

指针与指针变量

 
指针就是地址 

指针变量就是存放指针的,即存放地址的

任何一个变量都对应着一个地址/指针,比如int a=1;
符号a就对应着一个地址,&a表示其符号a的地址,
a=1;表示将1这个数字赋值到符号a对应的地址上

指针变量也对应着一个地址,该地址中存放的是地址/指针 

 
#include <stdio.h> 
int main(){
    int a=1;
    int* b=&a;
    printf("%p\n",b); //0x7ffcb9d9cc8c
    printf("%p\n",&b);//0x7ffcb9d9cc80
    return 0;
}

 
符号b是一个指针变量,它的值是一个地址/指针,
符号b本身也有一个地址0x7ffcb9d9cc80,并且是整数

指向指针的指针变量

数据类型**:双星,指向指针的指针

 
终于来到了高能时刻... 

int a=1;
int* b=&a;

b是一个指针变量,其值是指针,通常提到一个变量其实意在指这个变量的值,
比如这里,b 指 变量a的地址

b本身也是有地址的,&b是取变量b的地址,这是一个值,也可以用一个变量来存储,这就是指向指针的指针

#include <stdio.h>
int main(){
    int a=1;
    int* b=&a;
    int** c = &b;
    printf("%p\n",b); //0x7ffec0cc0504
    printf("%p\n",&b);//0x7ffec0cc04f8
    printf("%p\n",c); //0x7ffec0cc04f8

    *(*c) =2;
    printf("a=%d\n",a); //a=2

    return 0;
}

对于整数来说,很少这么操作,请看下面的字符串:

经典的字符串传参

C语言经典字符串传参

 
#include <stdio.h>
void change(char** tmp){
    *tmp = "abc";
}
int main(){
    char* a;
    change(&a);
    printf("-%s-\n",a);//-abc-
    return 0;
}

 
change函数传递的是指针变量a的地址,即&a
进入函数再使用*,即*(&a),相当于直接修改了变量a 
*(&a) = "abc";
相当于 
a = "abc";

a = "abc";对应的操作是,向符号a对应的地址存入"abc"的首地址
("abc"返回的是字符串的首地址,因为变量a是指针变量,其值是要放指针的),

函数中的一系列操作,建立在对地址的操作上,而一个计算机的虚拟内存只有一个 
所以从函数中出来后,变量a的值也随之改变

 
这里change函数传参这个过程本身是值的复制,
&a是一个数,将这个数复制给另外一个变量,再取这个变量的值进程操作
只是先向上追溯一个向量的地址,将地址传进去后,再取其值,就回到该变量本身了
这样就做到了,在函数中,直接操作外部向量

这里说的参数值复制,意思是说*tmp与a的值是一样的,
即指向的地址相同,但它们不是一个符号,只是彼此等价,
说到底,函数并没有将变量a真的传递进函数,传递的只有地址... 
符号什么的,只是一个表示,人看着方便就行... 
你要是乐意,你可以将无数个符号指向同一个地址

函数中操作外部变量

 
这个过程并不复杂,只是字符变量本身多了一个指针,在形式上看着复杂了,其实这个过程就两步:
    1. 向函数上传入一个变量的地址,&变量
    2. 在函数中取出这个变量,*变量 
    
这样就做到在函数中直接操作外部变量了

 
#include <stdio.h> 
void ch(int* i){
    *i = 2;
}
int main(){
    int i=1;
    ch(&i);
    printf("i=%d\n",i);//i=2
    return 0;
}

字符指针的特殊

 
字符指针指向一个字符串的首地址 
"abc"的特殊在于,它本身多占一个字符'\0' 
输出字符串时,从其首地址开始,遇'\0'字符结束,这个过程是自动的 

为什么会这样,为什么要这样?

因为整数有固定长度,比如int占四个字节,
int*指针指向第一个字节的地址,自动向后移动三次,凑够四个字节,就凑齐了所需要的字节了

而字符串中的字符数字是不固定的,一个字符串与另外一个字符串的长度是否相等不确定,
它可以很短,也可以很长... 
所以,就使用'\0'这个字符来统一表示字符串的结尾,遇到这个就表示结束了

参考文章