地址:
在C语言中定义的每一个变量都拥有属于自己的存储单元的地址,
通常我们认为一个变量拥有两个值,一个左值(地址)一个右值(变量的值)
在scanf()中使用到的 取地址符(&),就是向这个地址中输入数据从而给变量赋值
访问地址 就需要用到指针,用于保存地址
对于对象的访问
1.直接访问:通过对象名去实现访问,但有作用域的限制
2.间接访问:通过地址去访问,不受作用域影响
指针
用于保存其他对象的地址值。
在32位的系统中占4个字节,64位的占8个字节
1)指针的定义
指针变量的定义和其他变量的相同也是: 变量的类型 变量名= 初始值
int * p = NULL; 指针
这里表示定义了一个int * 类型指针p;他初始化直向空(空指针),
还有一种定义: int * p ; 这种定义,没有赋初始值,他是一个(野指针)指向未知,不知道是否能访问,能否修改。通常我们都是先定义空指针。在给他赋值。
2)与指针相关的运算:&取地址符 *指向运算符
在1中定义p 需要赋值(或者申请空间malloc),才能实现他的作用
赋值 int a = 5;
p = &a; //此时将a的地址付给了p,也可以说是p指向了a。
此时 *p == 5;
两个符号可以约掉 *&p == p 但是&*不能
3)指针变量作为函数的参数
形参 = 实参的值,
指针传入的是地址值,形参和实参指向的是同一个地址,改变形参的值同时会时实参的值发生变化。
在实现交换两个数的值时,传入地址,改变地址对应的值,可以实现交换,,但是注意的是,有题目是交换了传入的地址,也就是两个形参的地址做了交换,这样并不能交换实参的值。
4)数组与指针
数组元素是和普通元素是一样的,数组元素也有它自己的地址。
数组元素也有左值和右值,只不过数组的每一个元素的地址是相邻。
数组名 可以代表首元素的地址。
int a[10];
a => &a[0],数组名a当作指针来看。
指针加减,看当前的指向,如一维数组的p=>a =》a[0] p+1 =>a[1];
5)指针常量和常量指针
指针常量: 指针本身不能改变的,但是指向的空间里面
的内容可变。
如: 数组名 int * const a;
常量指针:是指向常量的指针。
指向的对象是常量,那么这个对象不能够改变的。
指针本身可以被改变,但是指向的空间的内容不能改变。
const int * a;
int const * a;
6)数组名和指针
数组名是一个指针常量。
数组名可以代表整个数组,但有些情况下是当作指针来看的。
如数组 : int a[10];
a <=> &a[0];
a代表的是a[0]的地址 a+1代表的是a[1]的地址。
此处的+1移动的是多长需要看当前a代表的是什么来决定。
如 : int a[3][4];
此时 a 代表的就是&a[0] a+1 =&a[1] 看成3行4列,可以理解为。首先指向的是第0行,加1 ,指向的是第1行,其地址在数值上等于他们那一行的第一个元素的地址。
如:一维数组
int a[5] = {1,2,3,4,5};
int * ptr = (int *)(&a + 1); //此时ptr指向了a数组后面的空间
printf("%d %d\n", *(a + 1), *(ptr - 1)); // 2 5
如:二维数组
*(a[1] + 2) 此时 a[1] 应该当作指针来看
*(&a[1][0] + 2)
=> *(&a[1][2])
=> a[1][2] //!!!!表示第1行第2列的那个元素 (不要写代表值)
练习题:
**a[3][4] 写出表达式的含义 **
&a[1][0]: 第1行第0列的元素的地址
a[2]: (1)代表a[2]这一个数组 (2)代表第2行第0列元素的地址
&a[0] + 1: =》 &a[1] (1)第一行的地址
*(&a[1][0] + 1): 代表第1行第1列的那个元素
7)指针数组 与 数组指针
a)指针数组:
指针数组是一个数组,不过数组中存放的全是指针
定义如:int(任意类型都可以) * p[10]; 相当于一个数组里面存放了10个int * 类型的数据
b)数组指针:
数组指针是一个指针,用于指向一个数组
定义: int (*p)[10]; 指向一个有10个元素的数组 <=> int a[10];
8) 字符串与指针
字符串就是又一串字符组成 (String类型????C语言中好像还没涉及到这个类型)
字符串的表达方式有两种
a)字符数组
char a [6] = {'a','a','a','a','a','\0'};
b)字符指针
char * p = "abcd"
字符串末尾会默认为"\0" 表示字符串结束
字符串我们只需要保存他的首地址即可,读取时会一直读到0结束
在C语言里面字符串是保存在一个叫做 .rodata(只读数据)的内存区域。
只读区域无法修改
char * p = "123456";
此时执行 *(p+1)=‘a’; 会出现段错误,因为此区域的数据是无法修改的。
字符数组
char a [6] = {'a','a','a','a','a','\0'};
//与普通的数组相同,保存在一个.data/栈空间,数组区域数据是可读可写的
同时大小也是与数组相同
sizeof(a)=6;
注意!!!使用sizeof时
char s[] = {'a','b','c','d','e'};// 这种末尾没有0
sizeof(s) = 5
char s[] = {"abcde"}; //这种末尾会添加0
=> char s[] = {'a','b','c','d','e','\0'};
sizeof(s) = 6
《strlen 计算的值不包括0》
9) 字符串常用函数
(1) strlen: 用来求一个字符串的长度
头文件: #include <string.h>
计算字符串的长度,不包含'\0'
l = strlen("abcd\nabc"); == 8 // \n看成一个?? \大多数都是表示转义
l = strlen("123\0123abc\0abc"); == 8 //读到0结束,但是第一个\0并不是末尾,
我们碰到这样的需要判断他是否为8进制的树,此时\012是当成8进制来看的。
\XXX 可接一个、两个或三个八进制的数
(2)strcpy/strncpy 字符串拷贝
头文件: #include <string.h>
strcpy(char *dest, const char *src);
复制 一个字符串到另一个字符串,都是从头开始
strcpy(没有考虑越界)strncpy增加了长度限制,
(1) 遇到\0拷贝结束,此时\0也会被拷贝
(2) 已经拷贝n个字符了(后面的\0不会自动拷贝,除非第n字符为\0)
(3)strcmp/strncmp
字符串比较,同样strcmpy是全部比较,strncmp是限制比较的长度
字符串比较是一个一个字符比较,
如果 c1>c2 返回 1,如果c1<c2 返回-1
如果相同则继续比较到下一个字符,知道结束,返回0
(4)strcat / strncat
源字符串空间应该足够大。将字符串链接到尾部。
同样 拷贝到\0结束,strncpy限制大小