C 99 据说是1999年所定义的
引入头文件
里面只有函数的声明,没有实现,编译的时候,会去对应的so文件找到函数的实现
#include <stdio.h>
基本数据类型
有符号(默认定义变量就是有符号)和无符号
signed 有符号 unsigned无符号
- int %d
- short
- long
- float
- double
- char
- bool 非0 就是true 0是flase 非Null就是true 等于null就是false
类型 | 占位 | 字节 |
---|---|---|
short | %d | 2 |
int | %d | 4 |
long | %ld | 4 |
float | %f | 4 |
double | %lf | 8 |
char | %c | 1 |
字符串 | %s | |
十六进制 | %x | |
八进制 | %o |
为什么 int 和 long 一模一样长,还有这两者的出现呢?
答:long类型 和 int类型,在早期16为电脑的时候 int是2字节,long是4字节,而计算机经过多年的发展,一般是32位,64位,就造成了int 和 long一样长了,如果想使用8位 则可以long long xxx 这样声明
使用sizeof来运算获得类型在不同平台准确字节
double d = 1.12342;
printf("double 字节%d\n", sizeof(d));
如果只取小数点后2位,只需要在%f前面加上.2
long double ld01 = 16.123456;
printf("ld01=%.2f,字节:%d\n", ld01, sizeof(ld01));
vim xxx.c 创建一个文件
编译c文件
gcc xxx.c -o xxx
字符串
字符串的拼接,需要定义一个数组
char strChar[200];
sprintf(strChar,"今天是%d号\n",11); // 拼接到char 数组
printf("%s\n",strChar);
数组
1、格式是这样的(array[] = {},array[6] = ),不能这样([] array)
2、必须给定 数组的大小,或者直接初始化
- 数组是一块连续的内存空间
- 数组内存地址和数组首位内存地址一样
int test[8];
int test[]={1,2,3,4,5};
for循环数组
int test[] = {1,3,4,2,4};
printf("数组首位地址:%#x\n",test);
for (int i=0;i< sizeof(test)/ sizeof(int);i++) {
printf("内存地址:%#x\n", (test + i));
printf("内存地址对应的值:%x\n", * (test + i));
}
数组首位地址:0x5f19f5f0
内存地址:0x5f19f5f0
内存地址:0x5f19f5f4
内存地址:0x5f19f5f8
内存地址:0x5f19f5fc
内存地址:0x5f19f600
动态内存申请 只要是动态内存申请,则在堆取,剩下的都在栈区
calloc
需要头文件
#include <stdbool.h>
//在堆中申请 10个int类型的内存空间,也就是10*4 = 40字节
int *p = calloc(10, sizeof(int));
// 清空内存,因为内存可能是别人剩下的,里面还会有原来的值
memset(p, NULL, 10 * sizeof(int));
//释放堆内存,申请了,用完要释放
free(p);
// 这个时候p 属于野指针,可以让p = 0 或者 null
p = NULL;
C 和 C++ ?
C面向过程,一个一个的函数,没有类,没有面向对象
C++面向对象 思想和 Java的一模一样,开始有类
字符串
使用字符数组存储字符串
//字符数组
char str[] = {'c', 'h', 'i', 'n', 'a', '\0'}; // 后面写0,就能结束
char str1[6] = {'c', 'h', 'i', 'n', 'a'}; // 直接写6个大小,字符5位,第6位就是0
char str2[10]="china"; // 直接双引号就是字符串
str[0]='w';
str1[0]='w';
str2[0]='w';
printf("str %s\n", str);
printf("str1 %s\n", str1);
printf("str2 %s\n", str2);
-------------------------------------
str whina
str1 whina
str2 whina
使用字符指针
char *str = "i XXX";
printf("*str %s\n",str);
*str i XXX
字符串指针和数组字符串 区别,字符串指针不能修改内存,数组可以
内存分配
C里面内存分配分为静态内存和动态内存
静态内存放在栈中,一般比较小,比如几M(存放确定的常数)自动释放,超过会有stack overflow错误,如
int a[1024 * 1024 * 10];
动态内存放在堆中,大小为内存的80%,程序员手动释放
创建一个数组,动态指定数组的大小,相当于java的集合
- 静态内存分配,分配内存大小是固定的。
问题:1、容易超出栈内存的最大值,从而stack overflow错误
2、为了防止内存不够用,会开辟更多的内存,容易浪费内存 - 动态内存分配,在程序运行过程中,动态内存需要指定内存大小,手动释放,释放后还可以继续使用
动态创建一个数组
int len;
printf("输入一个长度");
scanf("%d", &len);
int *p = malloc(len * sizeof(int));
int i = 0;
for (; i < len; i++) {
*(p + i) = rand() % 100;
printf("p==%d---%#x\n", p[i], &p[i]);
}
输入一个长度5
p==7---0x38500000
p==49---0x38500004
p==73---0x38500008
p==58---0x3850000c
p==30---0x38500010
动态创建数组,并改变数组的大小,
存在问题,
1、缩小数组大小(内存),缩小的数据会丢失
2、扩大,内存会是连续的
3、扩大,1.如果当前内存段后面有需要扩大的空间,直接扩展这段内存空间,realloc返回的指针,地址也是原指针的地址
2.如果当前内存段后面空间不够需要扩大的空间,那么就使用堆后面第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据释放掉,返回新的内存地址
3.如果申请失败,返回NULL,原来的指针仍然有效
int len;
printf("输入一个长度");
scanf("%d", &len);
int *p = malloc(len * sizeof(int));
int i = 0;
for (; i < len; i++) {
*(p + i) = rand() % 100;
printf("p==%d---%#x\n", p[i], &p[i]);
}
int len2;
printf("输入一个长度");
scanf("%d", &len2);
int *p2 = realloc(p, (len + len2) * sizeof(int));
i = 0;
for (; i < len + len2; i++) {
*(p2 + i) = rand() % 100;
printf("p==%d---%#x\n", p2[i], &p2[i]);
}
//p 不用再释放了,因为p2释放了,p也就释放了,p2本来就是对p的重新增长
//if (p != NULL) {
// free(p);
//p = NULL;
//}
if (p2 != NULL) {
free(p2);
p2 = NULL;
}
输入一个长度5
p==7---0x38500000
p==49---0x38500004
p==73---0x38500008
p==58---0x3850000c
p==30---0x38500010
输入一个长度3
p==72---0x38500000
p==44---0x38500004
p==78---0x38500008
p==23---0x3850000c
p==9---0x38500010
p==40---0x38500014
p==65---0x38500018
p==92---0x3850001c
内存分配细节
- 不能多次释放同一个指针,
- 释放完后,指针仍然有值,需要给指针=NULL
- 内存泄露,获取到的内存,如果需要再赋值,需要再赋值前进行free,要不相当于内存没有释放,系统认为还在使用
int *p1 = malloc(1024 * 1024 * sizeof(int));
free(p1); // 需要释放
p1 = NULL;
printf("p1 = %#x\n",p1);
p1 = malloc(1024 * 1024 * sizeof(int) * 2); // 重新赋值
printf("p1 = %#x\n",p1);
free(p1);
p1 = NULL;
头文件 .h文件,对外暴露 #include ""引入的是目录下可以看到的自己写的<>引入系统C配置的环境
引入头文件后,在.c文件中写头文件的函数,会有左右切换的符号,可以和头文件和当前写的文件进行交换。
image.png
之后再使用这个头文件,就直接在使用地方引入头文件,调用会调用实现头文件的.c文件
include 并不等于卷的import java.lang.String。
而是将include <xxx.h> 里的所有代码都复制一遍,放到include的地方。
所以如果不用就不要调用不用的h文件,要不会造成体积增大,在预处理器阶段就完成了,还不到编译器就结束了
宏 可以理解为java中的常量
宏不需要分号结尾
#define NAME "wx"
预处理器,代码的指向流程,可以作为代码块选择 或者 注释使用
if 和 #endif配套
void main() {
#define NAME "wx"
#if 0 // 非0 是true 0就是false 所以打印else if 但是预编译就直接有颜色显示
printf("打印 if \n");
#else if
printf("打印 else if \n");
#endif // 一定会执行
printf("打印 endif \n");
}
打印 else if
打印 endif
image.png
配合宏
#define DEBUG
//ifdef 表示有定义 ifndef 表示没有定义 n == not
#ifdef DEBUG
printf("配合宏,有定义DEBUG");
#else
printf("配合宏");
#endif
printf("必须执行");
#ifndef DEBUG
printf("配合宏,有定义DEBUG");
#else
printf("配合宏");
#endif
printf("必须执行");
image.png
网友评论