c语言基础

创建C语言程序

xcode -> create new xcode project -> macos -> command line tools
在macos系统即可创建一个c语言程序

基本数据类型

int 整型,占4个字节
double 双精度小数 占8个字节
float 单精度小数 占4个字节
char 字符 占1个字节

1
2
3
4
5
6
7
8
int i = 2;
printf("i的值:%d,所占字节%d\n", i, sizeof(i));
double db = 1.34;
printf("db的值:%lf,所占字节%d\n", db, sizeof(db));
float fl = 1.12;
printf("fl的值:%f,所占字节%d\n", fl, sizeof(fl));
char ch = 'A';
printf("ch的值:%c,所占字节%d\n", ch, sizeof(ch));

打印结果:
i的值:2,所占字节4
db的值:1.340000,所占字节8
fl的值:1.120000,所占字节4
ch的值:A,所占字节1

内存地址

使用 & 变量 即可获取到变量的内存地址

1
2
int i = 2;
printf("i的值:%d,所占字节%d, 内存地址为%p\n", i, sizeof(i), &i);

i的值:2,所占字节4, 内存地址为0x7ffeefbff4c8

指针

&i 既是取出i的内存地址,也是i的指针。
或者换个说法:

1
2
int i = 10;
int * i_p = &i;

取出指针的值使用 *

  • i_p 既可取出值 i = 10;
1
2
3
4
int i = 22;
int * i_p = &i;
printf("i 的值:%d, i_p指针指向的值: %d \n", i, *i_p);
printf("i的地址:%p, i_p的地址%p \n", i_p, &i_p);

打印:
i 的值:22, i_p指针指向的值: 22
i的地址:0x7ffeefbff4c8, i_p的地址0x7ffeefbff4c0
可以是 & 对指针再次取内存地址

数组

1
2
int a[5];
printf("a的值:%p, a的内存地址:%p, a[0]的地址%p\n", a, &a, &a[0]);

打印结果:a的值:0x7ffeefbff4b0, a的内存地址:0x7ffeefbff4b0, a[0]的地址0x7ffeefbff4b0

可以看到 打印 a、&a、&a[0]出现的结果都一样。

1、为何a与&a一样
因为a是数组,除基本数据类型外的其他变量,变量本身就是指向内存地址的。所以 a == &a
2、为何&a与&a[0]一样
因为a是数组,数组内存连续,所以数组下标为0的内存地址就是数组本身的内存地址。

1
2
3
4
int j = 0;
for (j = 0; j < 5; j ++) {
printf("当前下标为%d,下标内存地址为%p\n", j, &a[j]);
}

打印结果如下:
当前下标为0,下标内存地址为0x7ffeefbff4b0
当前下标为1,下标内存地址为0x7ffeefbff4b4
当前下标为2,下标内存地址为0x7ffeefbff4b8
当前下标为3,下标内存地址为0x7ffeefbff4bc
当前下标为4,下标内存地址为0x7ffeefbff4c0
可以看到内存地址以每次为4增加,因为这是int数组,int占4个字节,所以每次增加4

指针偏移

1
2
3
int * p = &a[j] + 1;
printf("p的值为:%p\n", p);
*(&a[j] + 0) = j;

&a[j] 是取出下标j处的地址。
&a[j] + 1 是指针在a[j]处偏移1位,既指向a[j + 1];
*(&a[j] + 0) = j; + 0 其实相当于未偏移,就是对a[j]赋值

函数指针

首先函数指针的申明格式:函数返回值类型 (* 指针变量名) (函数参数列表);

1
2
3
4
5
6
7
8
9
10
11
12
13
void add(int a, int b){
printf("sum = %d\n", a+b);
}

void test(void (*method)(int, int), int a, int b) {
method(a, b);
}

int main() {
printf("Hello \n");
test(add, 3, 4);
return 0;
}

在test函数入参中,有一个函数指针,
在main函数中,调用test函数,并将add函数作为入参传递到test函数中。

多级指针

指向指针的指针就是多级指针

1
2
3
4
5
6
7
8
9
10
11
int main() {
printf("HELLO WORLD!\n");

int a = 0;
int * b = &a;
int **c = &b;
int *** d = &c;
printf("d的地址%p \n", d);

return 0;
}

还可以用来表示多维数组

静态分配内存

在c语言中,内存区域分为栈区、堆区

函数被调用时,就会进栈,函数执行完毕则会出栈,而在函数中默认方式申明生成的数据都是系统静态分配内存的,
当函数执行完毕,出栈后,在该函数中静态分配的内存会被回收,系统会自己执行,无需手动处理。
静态分配内存都是在栈区

栈区总容量较小,大约是2M,各个系统、设备可能不一致,超出会抛出异常

动态分配内存

动态分配内存需要手动调用API才可,动态分配的内存不会自动回收,动态分配的内存都是在堆区。
堆区总容量较大,至少超过40M,各个系统、设备可能不一致, 超出也会抛出异常。

调用动态分配内存:malloc(int size)函数
调用回收动态内存:free(void *);

需要额外引入依赖文件:#include <stdlib.h>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main(){
int num;
printf("请输入数字\n");
scanf("%d", &num);
printf("num的值为:%d\n", num);

int * arr = (int *)malloc(sizeof(int) * num);
int i = 0;
for (i = 0; i < num; i ++) {
arr[i] = i + 10000;
}
for (i = 0; i < num; ++i) {
printf("%d对应值为%d\n", i, arr[i]);
}
free(arr);
return 0;
}

重新分配

就是将之前动态分配的内存重新分配,使用:realloc(void *ptr, size_t size);
第一个参数:原来的内存指针
第二个参数:新的内存大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int * new_arr = (int *)realloc(arr, sizeof(int) * (num + new_num));
if(new_arr) {
int j = num;
for (; j < (num + new_num); j ++) {
new_arr[j] = j + 100000;
}
int i = 0;
for (; i < (num + new_num); i ++) {
printf("新数组第%d个值为%d\n", i, new_arr[i]);
}

free(new_arr);
new_arr = NULL;
}

题目
截取字符串中指定字符
char c = “ABCDEFGHIJK”;

截取第3位到第6位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>


void substring(char * result, char *str, int start, int end) {
for (int i = start; i <= end; ++i) {
printf("char:%c\n", *(str + i));
*(result++) = *(str + i);
}
}


int main(){
char * str = "ABCDEFGHIJK";
char * result;
substring(result, str, 3, 6);
printf("result:%c\n",result);
return 0;
}