C语言基础

来源:互联网 发布:web动画创建软件 编辑:程序博客网 时间:2024/05/28 01:35

一、
打印:
printf("hello world");
int %d
short %d
long %ld
float %f
double %lf
char %c
bool(c99)
地址 %#x


int num;
scanf("%d", %num);//标准输入


const int n = 10;//静态内存
int intArray[5];//数组,编译期间知道数组大小,提前分配空间,不能用变量,不能太大,静态堆栈有一定的大小(确定的),系统自动释放
intArray[0] = 1;//赋值
动态分配内存是在堆里面(连续的) malloc --> 需要手动释放free,否则内存泄漏


sizeof()计算基本类型或其他数据结构占用内存的大小 sizeof(int)、sizeof(Human)...


二、
指针:指针变量存储的是变量类型对应变量的值。
int *p;//int型的指针p,不是*p,既:指针变量,存储的是地址
int i = 5;
p = &i;//把i的地址赋值给p,既:p指向了变量(int型)i的地址。此时 *p == 10, *p 代表一个int型的数据


&:取地址的操作符,作用于变量或数组元素。
*:间接寻址或者应用运算符,获取(&)地址的值,获取指针的值
指针变量大小总为4,因为地址的大小就是4个字节


int a = 10;
int *p;
p = &a;//此时p指向a的地址
*p = *p + 10;//我们改变了p所指向地址的值,既改变了a的值,所以此时 a == 10 + 10;


(*p)++ == *p + 1;
*p++ == 把指针移动sizeof(类型)大小到下一个指向它的位置


数组于指针
int arr[10];
int *p = arr;//数组名就是数组的首地址,可以直接赋值给指针变量
arr[i] == *(p+i);就是位置i上面的值
&arr[i] == a+i == p+i;就是位置i的地址


指针与函数参数
int *p;
p的类型是 int *,既指针变量
*p的类型是 int,既指针变量p的值


char *cp;// p 和cp都是存储地址的指针,其大小都为4个字节,
其类型大小是其运算的基本单位,如:p++ -> p移动sizeof(int)大小个字节,cp++ -> cp移动sizeof(char)大小各字节


交换2个数的值:


int n, m;
n = 10;
m = 5;


(1): c语言里面是值传递
void swap(int a, int b)
{
    int temp;
    temp = a;
    a = b;
    b = tem;
}


swap(n, m);结果 n == 10, m == 5 没有交换
会复制一份n 和 m的值分别给a 和 b
此时 函数里面的a 和 b会是新的内存,所以不能交换n和m的值


(2):
void swap2(int *a, int *b)//形参的修改不能带来实参的修改
{
    int *temp;
    temp = a; //a的地址给了temp
    a = b;    //把b的地址赋值给a
    b = temp; //把temp的地址赋值给b
}
swap2(&n, &m);结果 n == 10, m == 5 没有交换


(3)
void swap3(int *a, int *b)//a、b是一个地址
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
swap3(&n, &m);结果 n == 5, m == 10;
把地址指向的内容交换了。


指针数组:
char *str[] = {"hello", "c", "world"};
strcmp:排序
str[i] 就是指针,存的是指针


数组指针:
char (*p)[5];
p指向的是数组,有5个元素
p是一个指针变量名,指向数组的首地址


二级指针:
int a = 100;
int *p;
p = &a;
int **p2;
p2 = &p;
p2:就是一个二级指针,里面存的是(一级)指针p的地址。
p的值 == *p2;
a的值 == *p == *(*p2);




三、函数指针
int add(int a, int b)
{
    return a + b;
}
int sub(int a, int b)
{
    return a - b;
}
int (*calc)(int a, int b);
calc = add;
calc(3, 5);结果就是8
calc = sub;为什么函数名可以赋值给一个变量?--->因为函数名就是函数的首地址,所以我们可以把函数赋值给对应类型的函数指针。
calc(3, 5);结果就是-2;


只要和函数指针返回类型、参数个数和参数类型都相同就可以代替其他函数实现相应的功能。
通用性:用void *代表类型
int (*calc)(void *a, void *b);
calc = (int(*)(void *, void *))add;//强制类型转换
calc(2, 4);
void (*calc)(void *a, void *b);
void 类型的指针:
指针变量都是4个字节,都是用16进制表示
强转就是 void * -> int *, char *, float *


区别:
int (*calc)(void *a, void *b);函数指针
int * p(int a, int b);一个返回值是int *的函数


四、
内存分配:
int a[1024 * 1024 * 40];会栈溢出,因为栈有一定的大小
动态分配
int *a = (int *)malloc(sizeof(int) * 1024 * 1024 * 40);


内存有3个区:
1:程序区 --> 程序的二进制文件,其大小由系统分配
2:静态存储区 --> 全局变量和静态变量,在程序编译时就生成好了
3:动态存储区:
  堆区:用于程序动态分配,由开发人员手动释放,不然就会导致内存泄漏。
  栈区:编译器动态分配,由编译器自动申请和释放。如:int a[10];
void * malloc(size_t size):
分配内存的单元是字节,大小是size,连续的内存。
申请失败,返回NULL。


int *b = calloc(4, 100 * sizeof(int));
void * calloc(size_t count, size_t size);
申请count个大小为size的连续空间,连续空间的大小是size,不是count * size,既:分配count个内存,每个内存都是连续的,并会初始化内存空间为0。
申请失败,返回NULL。


释放内存:(立即释放 java GC 不会立即释放,所以经常OOM,缓慢)
free(a); 
a = NULL;
动态申请的内存必须手动释放,并且不能重复释放,最后赋值为NULL。
不赋值为NULL的话,a就会是一个野指针。
申请和释放必须一一对应,重复申请中途不释放的话也会导致内存泄漏。


realloc:从新分配内存。


五、字符串
c语言没有string
表示字符串:
char ch[10] = {'c', 'h', 'i', 'n', 'a', '\0'};//‘\0’:文件结束标识。如果分配空间大了,并且不以\0结尾的话,那么字符后面的内存就是乱码。

char ch[10] = "china";可修改 ch[0] = 's';ch是不能被修改的,因为ch是一个常亮,存储的是首地址;其指向的内存空间里的内容是可修改的。

char *ch = "china"; ch[i]不能修改。因为“china”是一个常量,ch又没有申请空间,当字符常量赋值给ch时,ch就指向了一个常量区,所以ch不能修改。
可用strcpy来修改:
1、先申请内存空间
char *ch = (char*)malloc(10 * sizeof(char));
2、为ch的内存空间赋值,ch指向的内存空间不变
strcpy(ch, "china");
//ch = "china" 如果这样的话,ch就指向了常量区"china"的空间,原来申请的空间就不在了,并且没有被释放,还会导致内存泄漏。
3、修改值
ch[0]= a;
此时ch的值为:ahina


strcat:追加字符




六、结构体
定义:一系列不同类型的数据的结合
定义结构体:
struct Student
{
    char name[20];
    int age;
};//此时还没有分配内存空间


//锁定结构体变量的数量
struct{
    char name[20];
    int age;
}stu3;//stu3匿名结构体,还是全局的


struct Student stu1;//此时就分配了内存


类型 != 变量 
结构体名代表的只是结构体的类型,没有分配内存空间
结构体中的成员可以单独使用


初始化结构体:


struct Student stu = {"tom", 20};
或者
struct Student stu;
strcpy(stu.name, "tom");
stu.age = 20;
或者
struct Student
{
    char name[20];
    int age;
}stu = {"tom", 20};




结构体数组:
struct Student stu[3] = {{"tome", 10}, {"maik", 20}, {"jack", 23}};//按顺序匹配
或者
struct Student stua[5];
int i = 0;
for(i = 0; i < 5; i++)
{
    stua[i].age = i + 10;
    strcpy(stua[i].name, "tom");//name为指针的话,就可以直接赋值 char *name; stua[i].name = "tom";
}


结构体指针:
struct Student *stu;
初始化:
struct Student *stu = stua;//stua是结构体数组,数组首地址赋值
或者
stu = (Student *)malloc(sizeof(Student) * size);
memset(stu, 0, sizeof(Student) * size);//把内存全部赋值为0
int i = 0;
for(, i < size; i++)
{
    (stu + i)->name = "jack";
    (stu + i)->age = 20 + i;
    或者
    stu[i].name = "jack";//这里name是指针,直接赋值
    stu[i].age = 20 + 1;
}


结构体里面包含函数指针:
struct Man
{
    int age;
    char *name;
    int (*msg)(char *, int);
}


int message(char *msg, int age)
{
    printf("%s\n", msg);
    return 0;
}


struct Man man;
man.age = 25;
man.name = "tom";
man.msg = message;


调用:
man.msg("hello", 20);


结构体里面添加结构体指针变量:
如:链表
struct Node
{
    int data;
    Node *next;
};




七、typedef 命令
取别名,类似代理,可读性强
tpedef int _int;//把int取别名为_int,后面就可以用_int 声明变量
_int a = 10;//和 int a = 10 一样


typedef int (*FPI)(char *, char *);//定义函数指针
int say(char *a, char *b)
{
    rerurn 0;
}
FPI f = say;
f("hello", "world");


八、共用体 union
将不同的数据类型放到同一块内存里面,其内存大小是成员中最大内存的类型所占内存大小。


union MyUnion
{
    int a;
    char b;
    float c;
}


MyUnion union;
union.a = 10;
union.b = "c";
最近赋值那个变量才可用。


九、枚举
enum
{
    oneday; n
    twoday; n+1
    threeday; n+2
};


自增的


十、文件IO操作(文件:控制信息和内容信息)
读文件:磁盘(二进制) -> 文件缓冲区 -> 应用程序内存空间
在c语言中,二进制文件和文本文件的读写区别:
写 \n - > \r\n (二进制到文本时)
度 \r\n - > \n (二进制到文本时)




1、打开文件(文本文件)
char *path = "E:\\***.txt";
FILE *fp = fopen(path, "r");


char buff[50];
while(fgets(buff, 50, fp))
{
    printf("%s", buff);
}


fclose(fp);//关闭文件


2、写入文件(文本文件)
char *path = "E:\\***.txt";
FILE *fp = fopen(path, "w");


if(fp == NULL)
{
    printf("open fail");
    reutrn 0;
}
char *txt = "hello world, 我是 king";
fputs(txt, fp);
fclose(fp);


3、读二进制文件
char *path = "E:\***.txt";
FILE *fread = fopen(path, "rb");
char *buff[50];
int len = 0;
//fwite
while((len = fread(buff, sizefo(char), 50, fread)) != 0)
{
    
}
fclose(fread);


4、获取文件大小
char *path = "E:\***.txt";
FILE *fp = fopen(path, "r");
if(fp != NULL
{
    fseek(fp, 0, SEEK_END);//对fp做位移(以SEEK_END为基准,偏移0个字节)
    long filesize = ftell(fp);
}


十一、预处理(还没编译,在写代码阶段)
1、预处理阶段
#define NUM 5 --> NUM == 5
#define NUM 5; --> NUM == 5;
等价替换(结尾有分号也会一起当成整体)


#include "hello.txt";
在写include代码时就会把hello.txt里面的内容放到include的地方。


#define MAX(x, y) ((x) > (y)) ? x : y
int a = MAX(3, 5);


#define cplusplus
#ifdef cplusplus //判断是否定义cplusplus,定义了就会走ifdef里面的语句


    printf("c++");


#else


    printf("not c++");


#endif //endif必须和ifdef配对





原创粉丝点击