struct point{
int x;
int y;
};//定义平面上的点
struct rectangle{
struct point p1;
struct point p3;
}//已知斜对角两点,可定义一个矩形
如果有struct rectangle r;
,就可以有r.p1.x
、r.p1.y
、r.p2.x
、r.p2.y
如果有变量定义:
struct rectangle r, *rp;
rp = &r;
注意:r.p1.x
、rp->p1.x
是两种等价的形式。rp->p1->x
写法错误,因为p1不是指针。
//对嵌套的结构体赋值
struct rectangle r = {{-1,0},{{1,1}};
struct rectangle react[]={
{{-1,0},{{1,1}},
{{2,1},{{3,7}}
};
C语言提供 typedef 的功能来声明一个已有的数据类型的新名字
typedef int Length
使得Length
成为int
类型的别名
typedef int Length;
Length a, b, len;
typedef struct ADate{
int x;
int y;
} Date;//Date 为struct ADate 的新名字
int main()
{
Date test = {1, 2};//利用Date 定义结构体变量test
printf("x=%d y=%d\n", test.x, test.y);
}
//为字符串类型取名
typedef char *string;
string s = "hello";
typedef char strings[10];//string为长度为10的字符串类型
strings s2 = "world";
printf("s=%s s2=%s\n", s, s2);
union anelt {
int i;
char c;
} elt1, elt2;
elt1.i = 4;
elt2.c = 'a';
printf("size=%d\n",sizeof(union anelt));//size=4
上面代码的含义是anelt的成员是一个int
或是一个char
,union类型的大小由成员中最大值决定
typedef union{
int i;
char ch[sizeof(int)];
} CHI;
CHI chi;
chi.i = 1234;
int i;
printf("i=0x%X\n", chi.i);//1234的16进制为0x000004D2
for (i = 0; i < sizeof(int); i++){
printf("%02hhX", chi.ch[i]);
}
printf("\n");
x86cpu为小端机,内存存放数时 低位在前 ,故chi.ch[i]
输出了D2040000,说明chi.ch与chi.i公用同一处内存空间。
全局变量初始化:
1.没有做初始化的全局变量会得到0值,指针会得到NULL
2.只能用编译时刻已知的值来初始化全局变量
3.它的初始化在main函数之前
int f(void);
int gall = 12;
int main()
{
printf(" in %s gall1=%d\n", __func__, gall);//gall = 12
f();//gall = 14
printf(" in %s gall2=%d\n", __func__, gall);//gall = 14
}
int f(void){
gall += 2;
printf(" in %s gall_f=%d\n", __func__, gall);
}
全局变量的赋值不能是变量
int gall = 12;
int gall2 = gall;//gall是变量,会报错
int gall3 = f();//同样会报错
正确的做法是
const int gall = 12;
int gall2 = gall;
还需要注意,如果函数内部存在与全局变量同名的变量,全局变量会被隐藏
int gall = 100;
int f(void);
int main()
{
f();//i=2
f();//i=3
}
int f(void){
static int i=1;//不加static每次运行函数会初始化i为1
printf("&gall=%p &i=%p\n", &gall, &i);
i++;
printf("i=%d\n", i);
}
//&gall=403014 &i=403010刚好相差4个字节,故i为全局变量
返回指针的函数需要注意:
#define PI 3.14
#define PI2 2*PI
int main()
{
printf("%f\n", 2*PI2*3.0);
return 0;
}//输出37.68
#define PI 3.14
#define PI2 printf("%.2f\n", PI);\
printf("%.2f\n", 2*PI)
int main() { PI2; }//输出3.14和6.28
预定义的宏:
__LINE__
源代码文件的行号(整数)__FILE__
文件名称(字符串)__DATE__
编译文件时的日期(字符串)__TIME__
编译时的时间(字符串)__FUNCTION__ __func__
返回程序名(字符串)#define L(x) x*10
#define L2(x) (x)*10
printf("%d\n", L(2 + 5));//输出52
printf("%d\n", L2(2 + 5));//输出70
有两个文件get.c``main.c
分别为
void get(void){
printf("运行成功");
}
#include
#include "get.c"
int main(){ get(); }//运行成功
main.c
和get.c
为例子//main.c
#include
#include "get.h"
int main()
{
get();
}
//get.c
#include"get.h"
void get(void){
printf("congratulation!");
}
//get.h
void get(void);
my_multi.exe
gcc -g .\main.c .\get.c -o my_multi
lanuch.json
(前提是已经将mingw64\bin加入了环境变量)lanuch.json
找到"preLaunchTask"
并将改行注释掉"cwd"
行改为"${workspaceFolder}"
;然后将"program"
行改为编译执行文件目录"${workspaceFolder}/my_multi.exe"
CMakeLists.txt
在其中写入project(MYMULTI)
add_executable(my_cmake_multi main.c get.c)
//包含生成文件,两个需要编译的c文件
Ctrl+Shift+p
调出命令栏,输入cmake:Configure,选择gcc编译器。成功后会生成build文件夹cmake ..
再输入mingw32-make.exe
。终端最后一行显示Built target my_cmake_bulti则表示创建编译文件成功。"program"
后改为my_cmake_bulti.exe
文件的路径即可进行调试。不用cmake自己创建build文件。在终端project处输入
mkdir build
cd build
cmake -G "MinGW Makefiles" .. //这一步是为了使程序调用gcc编译器
mingw32-make.exe
printf %[flags][width][.prec][hIL]type
printf("%-9d\n", 123); //靠左对齐输出
123
printf("%9d\n", 123);
123
printf("%+9d\n", 123);//表示前面保留加号或减号
+123
printf("%-+9d\n", 123);
+123 //
printf("%09d\n", 123);//空的位置填上0
000000123
printf("%hhd\n",12345);//0x3039只取一个字节的部分39,变成十进制是57
hIL类型修饰
类型修饰 | 含义 |
---|---|
hh | 单个字节 |
h | short |
l | long |
ll | long long |
L | long double |
FILE *fopen(const char *filename, const char *mode)
输入文件的路径,打开方式,返回文件指针。"r"模式下,文件若不存在则返回NULL
模式 | 描述 |
---|---|
“r” | 打开一个用于读取的文件。该文件必须存在。 |
“w” | 创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。 |
“a” | 追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。 |
“r+” | 打开一个用于更新的文件,可读取也可写入(不删除原有数据)。该文件必须存在。 |
“w+” | 创建一个用于读写的空文件,若文件已经存在则删除已有内容 |
“a+” | 打开一个用于读取和追加的文件。没有则会新建一个文件 |
int fscanf(FILE *stream, const char *format, ...)
返回:如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF。
fp = fopen("F:\\VScode\\VScode-C\\test.txt", "a+");
printf("name\t num\t score\n");
for (i = 0; i < 3;i++){
fscanf(fp,"%d%s%d", &num, name, &score);
printf("j=%d\n", j);//j=3,返回成功酦醅和赋值的个数
printf("%s\t%d\t%d\n", name, num, score);
}
fclose(fp);
int fprintf(FILE *stream, const char *format, ...)
fp = fopen ("file.txt", "w+");
fprintf(fp, "%s %s %s %d", "We", "are", "in", 2014);//输出为We are in 2014
下面为示例:
#include
int main()
{
FILE *fp;
int i, n;
int num, score;
char name[20];
fp = fopen("test.txt", "w+");
printf("你想输入多少组数据:");
scanf("%d", &n);
for (i = 0; i < n;i++){
printf("姓名:");
scanf("%s", name);
printf("学号:");
scanf("%d", &num);
printf("分数:");
scanf("%d", &score);
fprintf(fp, "%s\t%d\t%d\n", name, num, score);
}
fseek(fp, 0, SEEK_SET);
printf("name\tnum\tscore\n");
for (i = 0; i < n;i++){
fscanf(fp, "%s\t%d\t%d\n", name,&num,&score);//fscanf会改变指针位置
printf("%s\t%d\t%d\n", name, num, score);
}
fclose(fp);
}
int sprintf(char *str, const char *format, ...)
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
FILE *fp;
fp = fopen("file.txt", "w");
char str[] = "This is runoob.com";
fwrite(str, sizeof(str), 1, fp);
fclose(fp);
注意:fprintf将每一位用ASCII码储存,然后txt将ASCII码翻译位字符输出;而fwrite是直接将字符写入,例如65,65为A的ASCII码,txt中写为A
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
FILE *fp;
char c[] = "This is runoob";
char buffer[20];
FILE *fp;
/* 打开文件用于读写 */
fp = fopen("file.txt", "w+");
/* 写入数据到文件 */
fwrite(c, strlen(c) + 1, 1, fp);
/* 查找文件的开头 */
fseek(fp, 0, SEEK_SET);
/* 读取并显示数据 */
fread(buffer, strlen(c)+1, 1, fp);
printf("%s\n", buffer);
fclose(fp);
int fseek(FILE *stream, long int offset, int whence)
a+
和a
模式打开的文档,SEEK_SET就是在追加的起始位置而非文档开头long int ftell(FILE *stream)
该函数返回位置标识符的当前值。如果发生错误,则返回 -1L,全局变量 errno 被设置为一个正值。因此ftell可以得到文本大小
#include
int main()
{
FILE *fp = fopen("test.txt", "w+");
int loc = ftell(fp);
printf("loc=%d\n", loc);//loc=0
fprintf(fp,"abcde");
int loc2 = ftell(fp);
printf("loc2=%d\n", loc2);//loc2=5
}
若(x)i=1且(y)i=1,则(x&y)i=1,否则(x&y)i=0
0xFF
对某个数做按位与运算,可以取得其后8位bit的情况0xFE
可以使数的最后一位bit为0(~x)i=1-(x)i
c = 0xaa 10101010
~c = 0x55 01010101
-c = 0x56 01010110 //-c=2^8-c
若(x)i=(y)i,则(xy)i=0,否则(xy)i=1
1^0^0=1
0^0^0=0
1^1^1=1
0^1^1=0
(x^y)^y=x
x^(~x)=0xFF
i<
右移:unsigned型左填0;signed型左填原来的最高位。小于int类型的数以int来做
unsigned char b = 0xA5;
printf("b<<2=%hhx\n", (char)b<<2); //0x94
int a = 0x80000000; //2^31,signed型,保留第32个bit上的1,右移后第31个bit也变为1,结果为-2^30
unsigned int d = 0x80000000;//直接右移,最高位bit变为0
printf("a=%d\n", a>>1);//-1073741824
printf("d=%d\n", d>>1);//1073741824
下面的struct结构定义位段,每一位为1bit,从右向左排列,当所需位超过一个int时会采取多个int
#include
struct U0{
unsigned int leading : 3;
unsigned int FLAG1 : 1;
unsigned int FLAG2 : 1;
int trailing : 27;
};
void prtbin(unsigned int number);
int main()
{
struct U0 uu;
uu.leading = 3;
uu.FLAG1 = 1;
uu.FLAG2 = 0;
uu.trailing = 0;
printf("sizeof(uu)=%d\n", sizeof(uu));
prtbin(*(int *)&uu); //将uu指针变成整型指针输出,结果前五位01011
}
void prtbin(unsigned int number) //以二进制方式打印整数
{
unsigned m = 1u << 31;
for (; m;m>>=1){
printf("%d", number & m ? 1 : 0);
}
printf("\n");
}
#include
int main(){
int n=0xaaaaaaaa ;//10101010101010101010101010101010
//scanf("%d", &n);
unsigned m = 1u << 31;
for (; m;m>>=1){
printf("%d", n & m ? 1 : 0);
}
printf("\n");
}
设计一组函数,他们能提供可变大小的数组。他们需要满足:可变大小、能得到当下数组的大小、能获取里面的元素
PS:空指针表示==“未分配”或者“尚未指向任何地方”。它与未初始化的指针有所不同,空指针可以确保不指向任何对象或函数==,而未初始化指针可能指向任何地方。空指针与任何对象或函数的指针都不相同,malloc成功调用和&成功取址都不会得到空指针
每一个节点都是结构体,包含两部分,一部分位值value,一部分为指向下一个结构体的指针
#include
#include
typedef struct _node{
int value;
struct _node *next;
} Node;
int main()
{
Node *head = NULL;//定义指向第一个节点的指针head
int number;
do{
scanf("%d", &number);
if (number!=-1){
Node *p = (Node *)malloc(sizeof(Node));
p->value = number;
p->next = NULL;
Node *last = head; //让last从head通过循环到达最后一个节点
if (last){
while(last->next){
last = last->next; //保持last为最后一个节点的指针,next为NULL
}
last->next = p;//将最后一个节点中的next指向新的节点
} else {
head = p; //注意在第一次赋值时将指针p交给head
}
}
} while (number == -1);
}
include<stdio.h>
#include
typedef struct _node{
int value;
struct _node *next;
} Node;
typedef struct{
Node * head;
}List;
//Node *add(Node *head, int number);
void add(List* plist, int number);
int main()
{
List list;
list.head= NULL;
int number;
scanf("%d", &number);
while(number!=-1){
//head = add(head, number); //对链2表输入值,输入-1表示结束
add(&list, number);
scanf("%d", &number);
}
Node *p;
int count = 0;
for (p = list.head; p;p = p->next,count++){
printf("%d:%d\n", count+1,p->value);
}
Node *q;
printf("请输入你想删除的数:");
scanf("%d", &number);
for (q = NULL, p = list.head; p;q = p,p = p->next){
if(p->value==number){ //注意保证箭头左侧的指针不能为NULL
if(q){
q->next = p->next;//找到之后将p之前节点的结构体的next变量换成p之后节点的指针
} else{
list.head = p->next; //当需要删除的节点位于第一个位置时
}
free(p);
break;
}
}
count = 0;
for (p = list.head; p;p = p->next,count++){
printf("%d:%d\n", count+1,p->value);
}
for (p = list.head,q = NULL; p;p = q){ //删除整个链表
q = p->next;
free(p);
}
}
void add(List* plist,int number){
Node *p = (Node*)malloc(sizeof(Node));
p->value = number;
p->next = NULL;
Node *last = head;
if(last){
while(last->next){
last = last->next;
}
last->next = p;
}else {
head = p;
}
}
/* Node* add (Node*list,int number);函数有一个缺点,他是由另一个指针变量接受head,若不反回head,则可能因为最初head=NULL使程序错误
一个好办法是定义一个结构体
typedef struct{
Node * head;
}List;
然后传入List *plist,在add函数中对plist->head = p,这样add类型可以是void*/