QX-AI
GPT-4
QX-AI初始化中...
暂无预设简介,请点击下方生成AI简介按钮。
介绍自己
生成预设简介
推荐相关文章
生成AI简介

整数运算

两个整数运算结果也只能是整数
a+=5等价于a=a+5
a=b+5等价于a=a(b+5)
a++等a+=1等a=a+1
a++是a加1以前的值,++a是a加1后的值
运算符优先级:算数>关系>赋值,判断是否相等的优先级比大于小于低


所表达的数的范围

char< short<int< float<double


sizeof()

sizeof()是一个运算符,给出某个类型或变量在内存中所占据的字节数
sizeof()是静态运算符,它的结果在编译时刻就决定了,不要在 sizeof的括号里做运算,这些运算不会做的


unsigned

unsigned:如果一个字面量常数想要表达自己是 unsigned,可以在后面加u或U,255U用l或L表示long(long)
unsigned的初衷并非扩展数能表达的范围,而是为了做纯二进制运算,主要是为了移位


整数的输入输出

只有两种形式:int或 long long
%d: int
%u: unsigned
%ld: longlong
%lu: unsigned long long


选搔整数类型

为什么整数要有那么多种?为了准确表达内存,做底层程序的需要
没有特殊需要,就选int
现在的CPU的字长普遍是32位或64位,一次内存该写就是个int,一次计算也是一个int,选择更短的类型不会更快,甚至可能更慢
现代的编译器一般会设计内存对齐,所以更短的类型实际在内存中有可能也占据一个int的大小(虽然sizeof告诉你更小)


unsigned与否只是输出的不同,内部计算是一样的
printf输出inf表示超过范围的浮点数:±∞
print输出nan表示不存在的浮点数
带小数点的字面量是 double而非 float
float需要用或F后綴来表明身份
1.234f


判断两个浮点数是否相等可能失败
fabs(fl-2)< le-2//两个浮点数相减的绝对值小于一个很小的数,可认为两个浮点数相等


选浮点类型

如果没有特殊需要,只使用 double
现代CPU能直接对 double做硬件运算,性能不会比float差,在64位的机器上,数据存儲的速度也不比float慢


自动类型转换

当运算符的两边出现不一致的类型时,会自动转换成較大的类型
大的意思是能表达的数的范围更大
char——> short——>int——>long——>long long
int——> float——> double
对于 printf,任何小于int的类型会被转换成int,float会被转换成 double
但是scanf不会,要输入short,需要%hd


强制类型转换

要把一个量强制转换成另一个类型(通常是較小的类型),需要:(类型)值
比如:
(int)10.2
(short)32
注意这时候的安全性,小的变量不总能表达大的量
(short)32768
只是从那个变量计算出了一个新的类型的值,它并不改变那个变量,无论是值还是类型都不改变
强制类型转换的优先级高于四则运算


逻辑运算符优先级:

!>&&>||


短路

逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果了,就不会做右边的计算
a==6&&b==1
a==6&&b+=1
对于&&,左边是 false时就不做右边了
对于‖,左边是true时就不做右边了


tip1

不要把赋值,包括复合赋值组合进表达式!
一个代码内有重复相似段是程序质量不良的表现
函数是一块代,接收零个或多个参数做一件事情,并返回零个或一个值


数组的大小

sizeof给出整个数组所占据的内容的大小,单位是字节
sizeof(a)/sizeof(a[0])
sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代吗


数组的操作

遍历数组:通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的正好是数组最大的有效下标
数组作为函数的参数时:不能在[]中给出数组的大小,不能再利用 sizeof来计算数组的元素个数!
数组作为函数参数时,往往必再用另一个参数来传入数组的大小

作为参数的指针

void f(int p)
在被调用的时候得到了某个变量的地址
int i=O; f(&i)
在函数里面可以通过这个指针访问外面的这个
函数参数表中的数组实际上是指针
sizeof(a)==sizeof(int
)
但是可以用数组的运算符进行运算


指针是const

表示一旦得到了某个变量的地址,不能再指向其他变量
int const q=&i;//q是 const q=26;//OK
q++; //ERROR


所指是 const

表示不能通过这个指针去修改那个变量(并不能使得那个变量成为 const)
const int p=&i p= 26; //ERROR!
i=26;//OK
P=&i;//OK


const

lnt i
const int p1 =&i
int const
p2=&i
int const p3=&i
判断哪个被const了的标志是const在
的前面还是后面
const p指针不许动
const
p指针不许动变量


转换

总是可以把一个非 const的值转換成 const的
void f(const int* x)
int a =15:
f(&a);//ok
const int b = a
f(&b);//ok
b =a+1: // Error
当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比較少的字节数传递值给参数,又能避免函数对外面的变量的修改


const数组

const int a[]={1,2,3,4,5,6};
数组变量已经是 const的指针了,这里的 const表明数组的每个单元都是 const Int所以必须通过初始化进行赋值


保护数组值

因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
为了保护数组不被函数破坏,可以设置参数为 const
int sum(const int a[], int length);


指针

int p :指针加一p+1是指加上一个sizeof(int),将指针移到下一个单元
int
p=a[];
p —>a[0] (p+1)—>a[1]
*(p++)可以遍历数组


给一个指针加|表示要让指针指向下一个变量
int a[l0]
int p= a (p+1)—>a[1]
如果指针不是指向一片连分配的空间,如数组,则这种运算没有意义
(p+n)<—>a[n] p*q两个指针相减p-q,等于q加多少个单元等于p


p++
取出p所指的那个数据来,完事之后顺便把移到下一个位置去
的优先级然高,但是没有++高
常用于数组类的连续空间操作
在某些CPU上,这可以直接被翻译成一条汇编指令

指针乘除无意义


指针遍历数组方法

1
2
3
4
5
6
7
8
9
*p a[10]
p=a
for(i=0;i<sizeof(a)/sizeof(a[0]); i++ ){
printf("%d\n", acri[i]);
}
a[9]=-1//在数组末尾放入一个特殊的东西
while(*p!=-1){
printf("%d\n",*p++);
}

指针比較

<,<=,==,>,>=,!=都可以对指针做
比较它们在内存中的地址
数组中的单元的地址肯定是线性从小到大递增的


0地址

当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
所以你的指针不应该具有0值

因此可以用0地址来表示特殊的事情:
1返回的指针是无效的
2指针没有被真正初始化(先初始化为0)

NULL(必须是大写)是一个预定定义的符号,表示0地址
有的编译器不愿意你用0来表示0地址


指针的类型

无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
但是指向不同类型的指针是不能直接互相赋值的
这是为了避免用错指针

指针的类型转换

void表示不知道指向什么东西的指针
计算时与char
相同(但不相通)

指针也可以转换类型
intp =&i
void
p<—>(void *)p
这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
我不再当你是int,我认为你就是个void!


用指针来做什么

需要传入較大的数据时用作参数
传入数组后对数组做操作
函数返回不止一个结果是
需要用函数来修改不止一个变量
动态申请的内存


内存操作

malloc(跟系统要一块内存)

1
2
3
4
#include <stdlib. h>
void*malloc(size_t size)
//向malloc申请的空间的大小是以字节为单位返回的结果是void,需要类型转换为自己需要的类型
(int *)malloc(n*sizeof(int))

如果申请失敗则返回0,或者叫做NULL


free()

把申请得来的空间还给“系统”
申请过的空间,最终都应该要还
只能还申请来的空间的首地址

free常见问题

申请了没free—>长时间运行内存逐漸下降
新手:忘了
老手:找不到合的free的时机
free过了再free
地址变过了,直接去free


字符串

char a[]={‘h’,’a’,’l’,’l’,’o’,’!’}//字符数组
char a[]={‘h’,’a’,’l’,’l’,’o’,’!’,’\0’}//字符串


以0(整数0)结尾的一串字符
0或’\0’是一样的,但是和0不同
0标志字符串的结束,但它不是字符串的一部分
计算字符串长度的时候不包含这个0
字符串以数组的形式存在,以数组或指针的形式访问
更多的是以指针的形式
string.h里有很多处理字符串的函数


1
2
3
char*str="Hello"
char word[]="Hello"
char linel[10]="Hello"

字符串常量

Char s=”Hello, world”
●s是一个指针,初始化为指向一个字符串常量
●由于这个常量所在的地方,所以实际上s是 const
char
s,但是由于历史的原因,编译器接受不带const的写法
●但是试图对s所指的字符串做写入会导致严重的后果


char S[]=”Hello, world”//这个字符串就在我这里
Char* s=”Hello, world”//指向某个地方的字符串
数组:这个字符串在这里(作为本地变量,空间自动被回收)
指针:这个字符串不知道在哪里(处理参数,动态分配空间)
如果要构造一个字符串一>数组
如果要处理一个字符串一>指针


字符串输入输出

1
2
3
4
5
6
7
char string[8];
scanf("%s", string);
printf("%s", string);
//scanf读入一个単词(到空格、tab或回车为止)
//scanf是不安全的,因为不知道要该入的内容的长度
scanf("%7s", string)
//在%和s之间的数字表示最多允许该入的字符的数量,这个数字应该比数组的大小小

空字符串

1
2
3
4
char buffer[100]="";
//这是一个空的字符串, buffer[O]=="\0";
char buffer[]="";
//这个数组的长度只有1

复制一个字符串

1
2
char*dst=(char*)malloc(strlen(src)+1)
strcpy(dst, src)

枚挙

枚挙是一种用戶定义的数据类型,它用关键字enum以如下语法来声明
enum枚挙类型名字{名字0,……,名字n};

枚挙类型名字通常并不真的使用,要用的是在大括号里的名字,因为它们就是就是常量符号,它们的类型是int,值则依次从0到n。如:
enum colors {red, yellow, green};
就创建了三个常量,red的值是0, yellow是1,而 green是2。
当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量值名字。

声明枚挙量的时候可以指定值
enum COLOR {RED=1, YELLOW, GREEN =5};


结构

声明结构的形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct point{
int X;
Int y;
};
struct point p1, p2
//pl和p2都是 point里面有x和y的值

struct {
Int x;
int y;
}pl, p2;
//pl和p2都是一种无名结构,里面有x和y

struct point {
Int x;
int y;
}pl, p2;
//pI和p2都是point, 里面有x和y的值t

和本地变量一样,在函数内部声明的结构类型只能在函数内部使用
所以通常在函数外部声明结构类型,这样就可以被多个函数所使用了


结构指针

和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符

1
struct date*pdate= &today;


结构作为函数参数

1
int numberofdays(struct date d)

整个结构可以作为参数的值传入函数
这时候是在函数内新建一个结构变量,并复制调用者的结构的值
也可以返回一个结构
这与数组完全不同


指向结构的指针

1
2
3
4
5
6
7
8
9
struct date {
int month;
int day;
int yeari;
}myday;
struct date *p =&myday;
(*p).month 12;
p->zmonth 12;
//用->表示指针所指的结构变量中的成员

全局变量初始化

没有做初始化的全局变量会得到0值
指针会得到NULL值
只能用编译时刻已知的值来初始化全局变量
它们的初始化发生在main函数之前
全局变量不应该和另一个全局变量有联系
同名变量,本地变量优先级高于全局变量,即本地变量隐藏了全局变量


静态本地变量(全局生存期,本地作用域)

在本地变量定义时加上 static修饰符就成为静态本地变量
当函数离开的时候,静态本地变量会銖存在并保持其值
静态本地变量的初始化只会在第一次进入这个函数时做(只做一次初始化),以后进入函数时会保持上次离开时的值
静态本地变量实际上是特殊的全局变量,它们位于相同的内存区域
静态本地变量具有全局的生存期,函数内的局部作用域
static在这里的意思是局部作用域(本地可访问)

不要使用全局变量来在函数间传递参数和结果
尽量避免使用全局变量
丰田汽车的案子
使用全局变量和静态本地变量的函数是线程不安全的


返回指针的函数

返回本地变量的地址是危险的
返回全局变量或静态本地变量的地址是安全的
返回在函数内 malloc的内存是安全的,但是容易造成问题
最好的做法是返回传入的指针


编译预处理指令

开头的是编译预处理指令

它们不是C语言的成分,但是C语言程序离不开它们


#define(纯文本替换)

define用来定义一个宏

define<名字><值>

注意没有结尾的分号,因为不是C的语句
名字必颁是一个单词,值可以是各种东西
在C语言的编译器开始编译之前,编译预处理程序
(cpp)会把程序中的名字换成值
完全的文本替换
acc-save-temps


如果一个宏的值中有其他的宏的名字,也是会被替换的
如果一个宏的值超过一行,最后一行之前的行末需要加 \
宏的值后面出现的注释不会被当作宏的值的一部分


预定义的宏

1
2
3
4
5
_LINE_
_FILE_
_DATE_
_TIME_
_STDC_

带参数的宏

1
#define cube(x) ((x)*(x)*(x))

在大型程序的代吗中使用非常普遍
可以非常复杂,如“产生”函数:在#和##这两个运算符的帮助下
存在中西方文化差异
部分宏会被inline函数替代


变量的声明

int i;是变量的定义
extern int i;是变量的声明


声明和定义

声明是不产生代码的东西
函数原型
变量声明
结构声明
宏声明
枚挙声明
类型声明
inline函数
定义是产生代码的东西
头文件放声明是规则


重复声明

同一个编译单元里,同名的结构不能被重复声明
如果你的头文件里有结构的声明,很难这个头文件不会在一个编译单元里被#include多次,所以需要”标准头文件结构”


标准头文件结构(宏的if)

1
2
3
4
5
6
7
8
9
10
#ifndef _LIST_HEAD_
#define _LIST_HEAD_
#include "node.h"
typedef struct _list{
Node* head
Node* tail
}List
#endif
//运用条件编译和宏,保证这个头文件在一个编译单元中只会被#include一次
#pragma once//也能起到相同的作用,但是不是所有的编译器都支持

链表

0建一个node结构
1首选有个node结构(作为节点),结构里有int(或者其他类型)变量去存数据,还有个同样结构的*next(next要等于下一个节点)(套娃)
2读入数据,并新建一个node结构p,要让一个node结构head始终等于链表第一个节点
3要有一个node结构last,每次要让last从head开始,直到last->next是空的,然后让next等于新的p结构(这样就链起来了)
4每次读入新的数据,就新建一个p,然后让last从head开始遍历链表,直到last->next是空的,然后然后让next等于新的p结构

要在函数内改变指针的指向,就要传指针的指针进去


遍历链表

1
2
for(p=list.head; p; p=p->next){}
//单用一个指针p可以遍历链表

但要让删除链表中某一结点,需要另一个指针q,q一开始为null,后来始终指向p前一个节点
当p找到了要删除的节点,就让q->next等于p->next,然后free(p)
for(q=null,p=list.head; p; q=p,p=p->next){}
当然还要判断链表的第一个元素是不是我们要删除的
如果是,就不能让q->next等于p->next,因为q一开始是NULL
我们应该让head->next等于p->next,然后free(p)

“.”一般情况下读作”的”。
“->”一般读作”指向的结构体的”。


清除整个链表

1
2
3
4
5
for(p=head;p;p=q){
q=p->next;
free(p)
}
//先让指针p等于head,让q始终指向p的下一个节点,然后free(p),再让p=q,p去等于下一个节点

main()

main(成为C语言的入口函数其实和C语言本身无关,你的代码是被一小段叫做启动代的程序所调用的,它需要叫做main的地方)
操作系统把你的可执行程序装载到内存里,启动运行,然后调用你的main函数
在不同操作系统,入口函数可能不是main()