指针基础||一篇文章带你看懂指针
1. 指针是什么
- 指针是个类型!,数据类型
- 类型是干嘛用的,标注数据长度,做语法提示的。
#include <stdio.h>
int main() {
char a = 1;
char *b = &a;
*b = 9999;
printf("%d\n", *b);
/*char是-127--127,指针也是数据类型,也会标注长度,大类型转小类型会截取*/
}
2.指针的值
指针的值有三种,句柄,地址,空指针
空指针
char * b = NULL;
空指针就是占位
地址
#include <stdio.h>
int main() {
char a = 1;
char *b = &a;
}
指针的值是一个地址,地址就是房间号,大小是八个字节
#include <stdio.h>
int main() {
char a = 1;
char *b = &a;
printf("%d", sizeof(&a));//值是8
}
这个大小,和操作系统有关,32位系统是4字节,64位系统是8字节。
32位操作系统最大内存是4g,大概,32位系统,指针大小是4字节,也就是个int大小,四字节是32位,指针是存地址用的,也就是说这个地址长度是32位,也就是能存的东西是32^2,也就是4294967296 这个数/8,就是536870912 /1024大概事是54288,这是kb,算下来内存4gb左右。
-
为什么32位的系统指针是四字节, 64位系统的指针是八字节?
因为32位字长的系统的虚拟地址空间是
2^32 - 1
为了表示所有的地址,所以指针的大小必须大于等于2^32 - 1
, 所以就是4字节(32位)64位系统同理
指针就是个数字,八字节,说他是地址,只不过里面存的是数字,大一点而已。
-
long long 也是八字节,指针和long long 有啥区别
那就是不同类型,对运算符计算结果不一样,
#include <stdio.h>
int main() {
int a = 1;
int *p = &a;
long long lp = (long long)p;
printf("p = %lld\n", (long long)p);
printf("lp = %lld\n", lp);
}//一个指针一个long long
#include <stdio.h>
int main() {
int a = 1;
int *p = &a;
long long lp = (long long)p;
printf("p = %lld\n", (long long)p);
printf("lp = %lld\n", lp);
p++;
lp++;
printf("new_p = %lld\n", (long long)p);
printf("new_lp = %lld\n", lp);
return 0;
}
int型指针,p+1其实是加4
long long类型指针p+1就是+8
加号运算符就是n+m *sizeof(type)
n是原始数字,m是加数,type是指针的类型
比如指针类型是int ,p+2就是p+8
概括:指针也是普通的数字,指针运算符结果和整形不同
*运算符
-
*星号是指针声明的符号
用int*类型保存了a的地址
星号出现在等号左边和类型声明结合的话,就是声明的,直接*,就是解引用
#include <stdio.h> int main() { int a = 1; int *p = &a; *p = 2; printf("%d\n", *p); }//这里操作的是a
多级指针
就是一个指针变量存了另一个指针变量的地址,这样一直套娃
指针常量和常量指针
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
const int *p = &a;
p = &b;
printf("%d\n", *p);
return 0;
}//常量指针 一个指针指向了常量
指针不是常量,指针指向的地址是常量,不可以修改
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
const int *p = &a;
p = &b;
//*p = 3; //不行
b = 3 // 正确
printf("%d\n", *p);
return 0;
}
可以理解为p是指针,通过p没法给a重新赋值,
const int *p和int const *p都是常量指针
#include <stdio.h>
int main() {
int a = 1;
int *const p = &a;
*p = 3;
printf("%d\n", *p);
return 0;
}
//
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
int *const p = &a;
//p = &b; 不行
*p = 3; //可以通过p修改他存的值
printf("%d\n", *p);
return 0;
} //指针常量
这个是指针常量,就是const 和p结合,
这里p是常量, p等于&b就不允许了,
const int * const p,就是怎么都不能修改的意思。 指向常量的常量指针
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
const int *const p = &a;
//p = &b; 不行
//*p = 3; //不可以通过p修改他存的值
printf("%d\n", *p);
return 0;
} //指针常量
指针数组和数组指针
指针数组
指针数组:就是一个数组,数组的各个元素都是指针值。
#include <stdio.h>
int main() {
int a = 0;
int b = 1;
int c = 2;
int d = 3;
int *p[4];
p[0] = &a;
p[1] = &b;
p[2] = &c;
p[3] = &d;
printf("%d\n", *p[0]);
printf("%d\n", *p[1]);
printf("%d\n", *p[2]);
printf("%d\n", *p[3]);
}
在这里我们要注意一下运算符的优先级,由于 * 的优先级比 [] 的优先级低,所以整个表达式中p要先与[4]做结合表示的是一个数组的元素,再与 * 结合表示每一个数组元素的类型是一个指针变量。
多维指针数组
#include <stdio.h>
int main() {
int array[2][3] = {
{ 0, 1, 2 },
{ 3, 4, 5 }
};
int *p[2][3];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
p[i][j] = &array[i][j];
}
}
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", *p[i][j]);
}
printf("\n");
}
}
数组指针
因为*优先级比[]低,所以要这么声明
char (*pa)[4]; //表示一个叫pa的指针存了 char[4]这个数组的首地址
pa是一个指针,里面存的是数组的首地址
数组指针和数组区别
- 运算符结果不一样
- 大小不一样
#include <stdio.h>
int main() {
char a[4] = {1, 2, 3, 4};
char (*pa)[4] = &a;
printf("a is %p\n", a);
printf("a[0] is %p\n", &a[0]);
printf("pa is %p\n", pa);
pa++;
printf("new_pa is %p\n", pa);
printf("数组的步长是%d\n", a[1] - a[0]);
printf("sizeof pa is %ld, sizeof a is %ld\n", sizeof(pa), sizeof(a));
}
参考: https://book.itheima.net/course/223/1263669610003230722/1263675595644137474
#include <stdio.h>
int main() {
int array[2][3] = {
{ 0, 1, 2 },
{ 3, 4, 5 }
};
//数组指针
int (*q)[3] = array;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", q[i][j]);
}
printf("\n");
}
printf("array is %p\n", array);
printf("a[0][0] is %p\n", &array[0][0]);
printf("q is %p\n", q);
q++;
printf("行步长 是%d, 列步长是 %d\n", &array[0][1] - &(array[0][0]), &array[1][0] - &(array[0][0])); //1, 3 3是因为跨了一行
printf("new_q is %p\n", q);
printf("sizeof q is %ld, sizeof array is %ld\n", sizeof(q), sizeof(array));
}
函数指针
指向函数的指针:
不同于指向数据类型的指针,函数指针指向一段可执行的代码的首地址,这段代码仍然占用了一块内存空间。很多人都说C语言是一种面向过程的语言[来源请求],因为它最多只有结构体的定义,而没有类的概念。根据本段所述,可以认为C语言能成为面向对象的语言,只是表述比较麻烦而已。[9]事实上很多开源程序都使用这种方式组织他们的代码。
函数指针变量的声明:
type (*function_name)(...args);
//ps 去掉type就跟函数声明一样了
//相当于定义了个变量,这个变量存了一个函数的地址,参数是arg, 返回值类型是type
#include <stdio.h>
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}
//请输入三个数字:1 2 3
//最大的数字是: 3
#include <stdio.h>
void inc(int *val)
{
(*val)++;
}
int main(void)
{
void (*fun)(int *);
int a=3;
fun=inc;
(*fun)(&a);
printf("%d", a);
return 0;
}
数组和指针
一、概念
- 数组:数组是用于储存多个相同类型数据的集合。
- 指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其它变量在内存中的地址。
二、赋值、存储方式、求sizeof、初始化等
-
赋值
同类型指针变量可以相互赋值,数组不行,只能一个一个元素的赋值或拷贝
-
存储方式
-
数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的,多维数组在内存中是按照一维数组存储的,只是在逻辑上是多维的。
数组的存储空间,不是在静态区就是在栈上。
-
指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。
指针:由于指针本身就是一个变量,再加上它所存放的也是变量,所以指针的存储空间不能确定。
-
-
求sizeof
-
数组:
数组所占存储空间的内存:sizeof(数组名)
数组的大小:sizeof(数组名)/sizeof(数据类型) -
指针:
在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。
-
-
初始化
略
-
传参
传参: 参考https://blog.csdn.net/cherrydreamsover/article/details/81741459
数组名是什么
-
数组名出现在表达式中时,绝大多数情况(除了数组名作为sizeof的操作数或者作为取地址&元素符的操作数)会被隐式转换为指向数组的首个元素的指针右值。
-
当数组名作为取地址&运算符的操作数,则表达式的值为指向整个数组的指针右值。
#include<stdio.h> int main() { char s[]="hello"; char (*p1)[6]=&s; //OK! printf("和&结合是 %p,数组名单独是%p\n", &s, s); //char (*p2)[6]=s; //compile error: cannot convert 'char*' to 'char (*)[6]' //char *p3=&s;//compile error: cannot convert 'char (*)[6]' to 'char*' }
根据上述C语言标准中的规定,表达式 &s 的值的类型是char ()[6],即指向整个数组的指针;而表达式 s 则被隐式转换为指向数组首元素的指针值,即 char 类型。同理,表达式 s[4] ,等效于表达式 *(s+4)。