c语言基础知识记录
一、数据类型
数据类型:本质是固定大小内存块的别名
变量:本质为一段连续内存空间的别名
int a; //告诉编译器分配4个字节的空间
int b[10]; //40字节
printf("b:%d, b+1:%d, &b:%d, &b+1:%d\n", b, b+1, &b, &b+1);
// b与&b一样,b+1为b的地址+4,&b+1为b的地址+40
// b 代表的是数组首元素的地址
// &b 代表的是整个数组的地址 数据类型:有10个int元素的数组
sizeof:计算数据类型占内存空间的大小,返回类型为unsigned int
typedef:给已经存在的数据类型起别名,自定义类型
typedef long long UU_LONG; //用于跨平台
二、内存模型
1.几个区
栈区:自动申请自动释放,主要是局部变量(函数参数形参)、数组(char str[]=“hello world”)
堆区:手动申请,手动释放
(1)c语言:malloc/free (函数)malloc不一定能分配成功,返回void *
注意: free释放的是ret指针指向的内存空间,但是ret指针本身是没有变化的
free(ret);
ret=NULL;
图解:
(2)c++:new/delete (操作符)new一定能分配成功
全局区:
int a = 100; //全局区 默认同下
extern int a = 100; //全局区 外部链接
static int b = 100; //静态全局区 内部链接 只能在当前文件中使用
2.方向
(1)栈的生长方向:取决于开口方向
(2)内存的生长方向
大端模式:低位字节——>高位字节 (下图右)
小端模式:低位字节——>高位字节 (下图左)
三、指针 指针变量
1.概念
- 指针是一种数据类型,用来保存内存地址
- 不管几级(
*
**
***
)、指向什么样的类型的数据,指针变量都是占4个字节 - 指针的意义:间接赋值
2.野指针
野指针:是指指向一个已经删除的对象,或者是未申请访问权限内存区域的指针
后果:内存泄漏
具体情况:
(1)指针变量没有初始化
// 错误做法
int* p;
printf("%d",*p);
所以一拿到指针变量就直接置空
(2)指针指向的内存释放了,但是指针没有置空
free(p)
只释放p指向的内存释放掉了,但是对p没有任何操作
// 正确做法
free(p);
p=NULL;
(3) 越界访问
// 错误
char str[3] = "abc";
字符串最后有一个\0
3.指针的步长
指针步长:指针+1(加了多少字节),取决于指针指向的数据的类型
char *p = NULL;
printf("%d\n",p); //0
printf("%d\n",p+1); //1
int *q = NULL;
printf("%d\n",q); //0
printf("%d\n",q+1); //4
char buf[1024] = { 0 }; //40字节
printf("%d\n",buf); //数组名 = buf首元素的地址 = buf数组的地址
printf("%d\n",&buf); //buf的地址
printf("%d\n",buf+1); //buf的首地址+1 (加了1个元素char)
printf("%d\n",&buf+1); //buf的首地址+1024 (加了1个buf=1024个char)
几个常用的库函数:
memcpy
:void *memcpy(void *str1, const void *str2, size_t n)
从存储区 str2 复制 n 个字节到存储区 str1
memset
:void *memset(void *str, int c, size_t n)
复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符
4.指针的特性
(1)输入特性
void p_intArray(int* arr /*in*/, int len /*len*/);
(2)输出特性
分配空间
void allocateSpace(char** p /*out*/); //把指针作为输出,从函数中输出一个分配的空间
四、引用(变量的别名)[c++]
int a;
int *p=&a;//指针有自己的指针名字和地址,存储了指向对象的地址(可以更改→指向别的对象的地址)
int &b=a;//引用即别名(一旦声明则不可变更) 引用b和被引用的a用同一个地址:符号表中2个变量名指向同一个地址
(int &b=a和int* const pp=&a;一样不能改变指向的对象,但能改变指向对象的值)
//不同在于b的地址就是a的地址 即&b == &a
//而pp有自己的地址 pp存放了a的地址 即pp == &a
符号表存放内容: 变量名 变量地址 【符号表生成后不再改变】
int a a a的地址
指针p: 指针名 指针地址 而指针指向的a地址(存放的数据)可以改变 【不安全】
引用b: 引用名b 引用对象a的地址→→→故引用对象不能改变 【安全】
1.数组引用
先定义数组类型,再创建引用
typedef int ARR_TYPE[3]; //ARR_TYPE[3]为有三个元素的int数组的别名
ARR_TYPE[3]& arrRef = arr; //arrRef为arr的引用
或者直接给数组取别名:
int(&rf)[3] = arr
2.函数中引用
函数中的引用:函数的参数、函数的返回值
(1)在函数作传参使用
传递一个引用→→必然存在且有效【安全但没有指针快,一个地址总比复杂数据传递快】(用的)
传递一个指针→→指针不一定有效【需要判断所传递指针是否非空】
五、学习记录
1. 字符串拼接整数
int num = 10;
const char *str1 = "helloworld";
char str2[32] = {0};
sprintf(str2,"%s%d",str1,num);
printf("%s\n",str2);
1)char *str与char str[]的区别
char str[] = "abcd"; //abc存储在堆栈中
第一个表达式表示的是
- 在动态变量区中开辟一个能连续放4个字符的数组,数组名称是str。
- 赋值运算符右边是一个字符串常量,这个字符串常量是存放在常量区的。
这个表达式的意思就是将“abcd”这个字符串常量拷贝到刚才开辟的数组中。
C语言规定,表达式如果是一个数组名,则代表的意思是该数组的起始地址,如果这个数组在一个函数中定义,如果以数组名返回时,因为数组在函数中定义,是个局部变量,函数返回之后,这个数组所占用的空间就被释放掉了,数组也被破坏掉了,因此返回的数组名也就没有意义,不能被其主调函数使用了。
char *str = "abcd"; //abc存储在静态存储区
这个表达式的意思是
- 在动态变量区中开辟一个存放指针的存储单元,指针变量名是str,这个指针指向的是字符串第一个字符的地址,而这个指针存在栈上;
- "abcd"同样也是一个字符串常量,存储在常量区,在程序的运行过程中一直存在。
把字符串“abcd”的地址值拷贝到刚才的存储单元中,即指针变量str的初值是字符串“abcd”的地址。
这时如果char *str="abcd"定义在一个函数中并且以return str返回,因为str是一个变量名,返回的仅仅是str的值,所以在其他函数中可以使用该值,照样能够访问到“abcd”这个字符串。
含义上的区别:
- 数组表示一块内存区域,其地址和容量在生命期里不会改变,只有数组的内容可以改变,赋值是在运行时进行的
- 指针指向一块内存区域,而它指向的内存区域的大小可以随时改变,赋值是在编译时进行的,而且当指针指向常量字符串时,它指向的内容(常量字符串)是不可以被修改的,否则在运行时会报错。
使用上的区别:
- 数组存储在栈区,系统会自动为其分配内存
- 指针存储在堆区,需程序员自己进行释放。如果只有定义,没有用char* sp=a;指向某个具体数组,则使用之前必须用malloc为他手动分配内存,使用完后用free释放
char *sp=(char*)malloc(sizeof(char)*(n));
sizeof的区别:
- 用运算符sizeof可以计算出数组的容量(字节数)
- 用sizeof却无法计算指针所指内存的容量,用sizeof§得到的结果永远是4或者2(即指针变量所占内存单元的字节数)。且在进行参数传递时,数组会自动退化为同类型的指针。
3)内存的分配方式
内存分配可分为三种:静态存储区、栈区、堆区。
-
静态存储区:该内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,它主要存放静态数据、全局数据和常量。
-
栈区:它的用途是完成函数的调用。在执行函数时,函数内局部变量及函数参数的存储单元在栈上创建,函数调用结束时这些存储单元自动被释放。
-
堆区:程序在运行时使用库函数为变量申请内存,在变量使用结束后再调用库函数释放内存。动态内存的生存期是由我们决定的,如果我们不释放内存,就会导致内存泄漏。
C编程语言本身对字符串的唯一支持是编译器会将带引号的字符串常量转换为以空字符结尾的字符串,该字符串存储在静态内存中。
2.char数组赋值
字符串只有在定义初始化的时候,可以用“=”赋值(char[4] s="abc"
);后面只能使用strcpy对字符数组进行赋值(strcpy(s,"abc"
)。