C语言提高
文章目录
1. typedef的用法
- 用于给数据类型定义别名
- 一般用于给结构体简化名称
- 也可增加代码移植性(我觉得将来用不到)
2. 函数与void
a.返回值类型
void function(){
XXXXXX;
return 3;
}
C语言对函数的检查不够严格,上面函数返回值写void,但是函数体里面去写了返回值,这显然是不正确的做法,但是在实际的运行过程中,却未必会报错,甚至没有任何警告
- 如果不写void,编译器会自动根据该函数是否存在返回值而默认添加返回值类型,但并不总是能够返回正确的结果,在有的编译器对于部分返回值,会出现意想不到的错误
- 如果写void返回值,而返回值类型并不是void,在不使用该返回值而仅仅是调用该函数的情况下,编译器只有警告,但可以正常运行。
b.传入参数
int f(void)
{
printf("函数调用\n\n");
return 5;
}
int main(void)
{
f(2);
system("pause");
return 0;
}
C语言对传入的参数的检查不严格
- 在定义函数传参为void时,调用函数是传入一个或多个参数只产生警告,而不报错,函数可以正常运行
3.typeof()不是一个函数
a.本质不是一个函数,而是一个操作符
b.使用方法
int main(void)
{
int a = 10;
printf("szieof int = %zd\n", sizeof(int)); //对数据类型的使用方式
printf("szieof a = %zd\n", sizeof(a)); //对变量的操作方式1
printf("szieof a = %zd\n", sizeof a); //对变量的操作方式2
system("pause");
return 0;
}
c.返回值类型—unsigned int
if (sizeof(int) - 10 > 0) //True
printf("sizeof(int) - 10 > 0\n");
堆和栈
1.栈的生长方向
栈底(高地址)–> 栈顶(底地址)
2.数据存储方向 — 小端对齐
高位(低地址) --> 地位(高地址)
即:数据由右向左存储
3.在堆中分配与管理内存空间(动态分配内存)
以分配四百个int大小的字节空间为例
- malloc()
不会为这快空间初始化数据
int * a = malloc(sizeof(int)*100)
- calloc()
为这块空间初始化数据为0
int * a = calloc(100, sizeof(int))
- realloc()
不会为新开辟的空间初始化数据
新的内存分配机制有三种:
机制一,如果空间变小:截断后面的数据,前面的空间构成新的内存空
机制二,如果空间变大:该内存后续空间充足,直接将内存空间往后延伸扩展,指针指向地址不会发生改变
机制三,如果空间变大:该内存后续空间不足,开辟新的空间,将原来数据拷贝到新空间,指针指向地址发生改版
???
虽然新开辟空间并没有被初始化,但是值大到一定程度时会发现,相当一部分数值为0,而且我在测试过程中,内存地址似乎一直没有改变,这是通过vs2017测试的结果,以上两点令我疑惑不解
???
int * a = malloc(sizeof(int)*95)
a = realloc(a, sizeof(int)*100)
堆与栈注意事项
1.不要用函数返回临时变量的地址
在使用函数返回函数体内的临时变量之后,该临时变量的内存空间已经被释放,i得到的地址已经没有意义了,及时系统为该值作了一次保留,但是在第二次使用时就会变成一个意料之外的固定数值
int * f(void) {
int a = 10;
return &a;
}
int main(void)
{
int * i = f();
printf("%d\n", *i); // i = 10, 系统为变量做了一次保留,第二次使用值就发生了改变
printf("%d\n", *i); // i = *********(两串相同的长数字)
printf("%d\n", *i); // i = *********
system("pause");
return 0;
}
2.不要将空指针作为实参传递给被调函数
在主调函数中的指针如果没有指向地址,作为参数传给被调函数时,背调函数的形参值依然为NULL,修改被调函数的指针地址并不能修饰到主调函数。
解决办法:形参设为高级指针,将指针变量的地址作为地址传递
const
- 在函数体内(包括主函数)用const修饰的变量为伪常量,在有些时候并没有常量所拥有的功能,虽然不能直接修伪常量的值,但是可以通过创建一个指针指向该常量,修改指针地址的值,就可以达到修改常量值得目的
- 在函数体外用const修饰的变量是真正的常量,不能被修改,即使通过指针的方法修改,语法可以通过,但是运行时会中断
const
修饰函数形参,使传递给函数的值不可修改(可以防止指针传递过来的数据被修改)
字符串常量
关于字符串常量的处理并没有设立标准,不同编译器有不同的实现方案
- 有的编译器把相同的字符串只设定一个地址,有的编译器则不作这样的优化
- 有的编译器可以修改字符串中的某个字符,有的编译器则不可以
- 由指针定义地字符串为字符串常量,存放在栈中,不可被修改
char * str = "Hello!"; //存放在栈中
指针
杂项
1.不能通过对指针赋值正整数的方法给指针指定一个地址,这是非法的,否则程序会在运行时中断
2.指针多指向的内存地址发生偏移之后,就不能利用该指针对原空间进行释放了,否则程序会在运行时中断
野指针
- 使用为未初始化的指针
- malloc被free之后,指向的这个内存的指针的值没有被设为
NULL
- 函数结束后,函数体内的静态变量的内存被释放,指向的这个内存的指针的值没有被设为
NULL
注:空指针(值为NULL的指针)可以被释放(free),野指针会运行报错
字符串
1.字符串的定义方法
char a[] = { 'h' , 'e', 'l', 'l' , 'o', '!', '\0' }; //最后需要添加\0结束字符
char b[10] = { 'h' , 'e', 'l', 'l' , 'o', '!' }; //默认没有赋值的后面几个字符都是\0
char * c = "hello!";
printf("%c\n", *c);
printf("%c\n", *b);
printf("%c\n", *a);
- 未指定长度的字符串数组必须在最后添加结束自负‘\0’,否则使用或者输出是会出现乱码
- 指定长度的字符的数组默认没有赋值的后面几个字符都是‘\0’
- 变量名保存字符串的第一个字符的地址,可以用*(变量名+n)取出其中的每一个字符
2.字符串的三种拷贝方法
a.通过数组方式获取单个字符逐个修改
void strcp1(char * newstr, char * sourcestr)
{
int len = strlen(sourcestr);
for (int i = 0; i < len; ++i)
{
newstr[i] = sourcestr[i];
}
newstr[len] = '\0'; //目标是个数组,其实可以不用加‘/0’
return;
}
b.通过指针操作获取单个字符地址逐个修改
void strcp2(char * newstr, char * sourcestr)
{
while (*sourcestr != '\0')
{
*newstr = *sourcestr;
newstr++;
sourcestr++;
}
*newstr = '\0'; //目标是个数组,其实可以不用加‘/0’
return;
}
c.类似于b,是一种更简洁更难理解的方法
while (newstr++ = sourcestr++){}
3.sprintf()&menset()
a.sprintf()作用
-
拼接字符串
-
数字转字符串
b.menset()作用
- 格式化字符串数组