1. 内存地址和内存空间
学习指针的相关知识,首先需要了解下内存。我们通常所说的内存,往往指的是内存空间。其实内存是由内存地址和内存空间共同组成的,且内存地址和内存空间是一一对应的。
内存地址是一个编号,代表一个内存空间的位置。
内存空间是存储数据的空间,真正保存数据的地方。
在计算机中存储器的容量是以字节为基本单位的。也就是说一个内存地址代表一个字节(8bit)的存储空间。
有个非常形象的比喻:
内存就像小区房子一样,内存地址代表房子的门牌号,内存空间是房子内居住的空间。
已经被出售了的,代表空间被申请占用了;
已经办理入住的,代表空间被数据写入;
正在出售的,代表此空间空闲;
2. 指针相关表示方法
-
2.1 指针,指向某空间的标记,比如:
char *charPointer —> charPointer是指向 char 类型的指针。char 内存中占用一个字节内存空间,charPointer指向了这个字节的内存地址
int *intPointer —> intPointer是指向 int 类型的指针。int 内存中占用四个字节内存空间,ntPointer指向了这个四个字节中首字节的内存地址。
2.2 取地址
符号 &,可以帮助我们获取相关内存中的地址。
指针就是先申请内存空间,然后把指向这个数据内存的首地址存入申请的内存空间。
所以指针必须初始化才有意义。
int a = 1;
int *d ; //申请一个可以保存 int 类型内存地址的指针
d = &a;// 把 a 的地址赋值给指针
printf("%d \n", a); // log结果:1
printf("%p \n", &a); //log结果:0x7ffeefbff59c
printf("%lu \n", sizeof(a)); //log结果:4
printf("%p \n", d); //log结果:0x7ffeefbff59c
printf("%lu \n", sizeof(d)); //log结果:8
- 2.3 解引用
我们已经知道指针的内存空间中保存了相关的数据,那么怎么来使用这个数据呢?这里需要的就是解引用,也就是间接访问。
需要注意的是:
NULL 指针 :什么都没有指向的指针(默认 NULL = 0)
int a = 1;
int *d ;
d = &a;
printf("%d \n", a); // log结果:1
*d = 5; //这里就是解引用(间接访问) :* 这个 * 是解引用指针
printf("%d \n", a); // log结果:5
3. 指针的相关运算
通过取地址,指针,解引用……相关的表示,我们衍射出很多相关的运算
- 3.1 通过指针操作相关的变量
int a = 12;
int *d = &a;// 创建指针 指向a的地址
*d = 10 - *d;// 这里其实就修改了a变量
printf("%d \n",a); // log 结果:-2
//先取地址 然后解引用
*&a = 25;// 这行等价于 a = 25;
printf("%d \n",a);// log 结果:25
printf("%p \n",&a);// 取a 的地址::0x7ffeefbff59c
//对这个地址数值进行操作
*(int *)0x7ffeefbff59c = 20; // (int *)0x7ffeefbff59c ->代表指针常量,这个是把内存地址强转成一个指针
printf("%d \n",a);//打印结果 :a = 20;
-
3.2 多级指针
多级指针是指,把一个变量的地址赋值给一个指针,然后把这个指针的地址又赋值给新的指针……多级指针的解引用,也是一层层通过相关的地址找到最终变量,然后才能对变量进行相关操作
int a = 12;
int *b ;
b = &a;
int **c;//指向指针的指针 :指向指针的地址
c = &b;
/* 解引用: **
第一个* 指向b 的内存地址,得到指针b
第二个* 指向a 的内存地址,得到变量a
*/
**c = 25;
printf("%d \n",a);//打印结果 :a = 25;
- 3.3 练习指针的++,--
char c1,c2,c3;
char *cp;
//定义初始化函数
void setup() {
c1 = 'a';
c2 = 'f';
c3 = 'c';
cp = &c1;
}
int main(int argc, const char * argv[]) {
setup();
printf("%c : %c : %c \n",c1,c2,c3);//a : f : c
printf("%p : %p : %p \n",&c1,&c2,&c3);//0x100001030 : 0x100001031 : 0x100001032
printf("%p : %p \n",&c1,cp);//获取 c1 cp 地址:0x100001030 : 0x100001030
printf("%p \n",&cp);//指针的地址:0x100001038
printf("%c \n",*cp);//指针指向的地址:a
printf("%c \n",*cp + 1);//指针指向的地址:b ;原因:a的asc值97 ,97+1 = 98 (b的asc值)
printf("%p : %p \n",cp + 1,&c2);//指针指向的地址:0x100001031 : 0x100001031
printf("%c \n",*(cp + 1));//指针指向的地址:f : 原因:指针指向的内存地址 + 1,指向了下一个byte的内存
printf("%p : %p \n",++cp,&c2);//0x100001031 : 0x100001031
// 练习 // 结果:
setup();
printf("%c \n",*cp);//a
printf("%c \n",++*cp);//b
setup();
printf("%c \n",*cp++);//a
setup();
printf("%c \n",*(cp + 1));//f
setup();
printf("%c \n",++*cp++);//b
setup();
printf("%c \n",++*++cp);//g
}
4. 指针与字符串
字符串指针->指向的是字符串第一个字符的内存地址;
字符串都是以"\0"结尾;
计算字符串长度的demo:
// 计算字符串长度
int my_strlen(char *string){
int length = 0;
while (*string++ != '\0') {
length++;
}
return length;
}
int main(int argc, const char * argv[]) {
char *name = "bill gates"; //指向字符串:指向的是第一个字符;
printf("%s \n",name);//bill gates
unsigned long length = strlen(name); // 字符长度 c系统自带函数
printf("%lu \n",length);//10
//自定义函数
int myLength = my_strlen(name);
printf("%d \n",myLength);//10
}
查找字符串中的某个字符demo
//查找字符串
int find_char(char **strings,char word,int count){
char *string ;
char character;
int num = 0;
while ((string = *strings++) && num++ < count) {
while ((character = *string++ ) != '\0') {
if ( character == word ){
return 1;
}else{
printf("继续查找……%c \n",character);
}
}
}
return 0;
}
int main(int argc, const char * argv[]) {
char *s0 = "zero", s1 = "first", s2 = "second",s3 = "third",*s4 = " ";
int total = 5;
char *strs[total];
strs[0] = s0;
strs[1] = s1;
strs[2] = s2;
strs[3] = s3;
strs[4] = s4;
char word = 'a'; //查找词
if ( find_char(strs, word,total) ){
printf("find_char :%c \n",word);
}else{
printf("unfind_char :%c \n",word);
}
}
5. 指针的运算常在数组中使用
算数运算:指针 +(-) 整数 ,指针 - 指针(只限于指向同一数组的指针)
单独的两个指针进行 加减乘除 是没有意义的
关系运算(只限于指向同一数组的指针): >, >=, <, <=, !=, ==
// 遍历数组
void logArray(char arr[],int count){
for(int i = 0 ;i < count ; i++){
printf("%d : %c \n",i,arr[i]);
}
}
int main(int argc, const char * argv[]) {
char *cp;
int total = 5;
char characters[total];
/*cp < &characters[total];
指针的逻辑比较
*/
for (cp = & characters[0]; cp < &characters[total]; cp++) {
*cp = 'a';
}
logArray(characters, total);
char * cBegin = &characters[0];
char * cEnd = &characters[total];
for (cp = cEnd; cp >= cBegin; cp--) {
*cp = 'b';
}
logArray(characters, total);
/*long count = cEnd -cBegin;
指针的加减,判断数组中元素间隔的距离
*/
long count = cEnd -cBegin;
printf("array count : %ld \n",count);
}
6. 指针和函数
- 6.1 函数的返回值可以是指针
int* find_intValue(int arr[] ,int arr_count ,int value ){
for (int i = 0 ; i
- 6.2 函数的参数也可以是指针
// 数据交换
void swap_0(int a ,int b){
int temp = a;
a = b;
b = temp;
}
void swap_1(int *a ,int *b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(int argc, const char * argv[]) {
//交换
int m = 10;
int n = 20;
// 传值 操作,并不能完成数据的交换
//参数把数值拷贝一份,函数内部进行了交换,不会影响到外部;
printf("swap_0交换前 m= %d : n= %d \n",m,n);
swap_0(m, n);
printf("swap_0交换后 m= %d : n= %d \n",m,n);
// 传址 操作,才能完成数据的交换
//参数把地址拷贝一份,函数内部根据地址获取变量进行了交换,所以在内存中数据发生了交换
printf("swap_1交换前 m= %d : n= %d \n",m,n);
swap_1(&m, &n);
printf("swap_1交换后 m= %d : n= %d \n",m,n);
/*log结果:传值操作和传址操作是明显不同的
swap_0交换前 m= 10 : n= 20
swap_0交换后 m= 10 : n= 20
swap_1交换前 m= 10 : n= 20
swap_1交换后 m= 20 : n= 10
*/
}