C语言概述

知识扫盲

CPU从内容读取指令,执行相应的操作.

计算机只能识别1和0指令

第一个C语言程序

连接:就是把我们自己写的.c编译后的.o文件和系统函数合并在一起,生成一个可执行文件

编译:

分步编译:
cc -c xx.c
cc xx.o -o name

快速编译:
cc xxx.c -o name  //快速编译,不会报错

执行:

./name

文件名:

.c 源文件
.o 编译后的文件  (windows  .obj)
.out 可执行文件  (windows  .exe)

基本语法

关键字

就是c语言提供的有特殊含义的符号,也叫做保留字

标识符

就是我们自己定义的一些符号和名称,用来区分某些东邪,比如函数名,变量名

标识符命名规则

  1. 只有26个字母,数字,下划线组成
  2. 严格区分大小写
  3. 不能以数字开头
  4. 不可以使用关键字
  5. 有意义

注释

注释用来表明一段代码有什么作用,不会被编译,还可以用来排错

可以写任何文字,一般用豆绿色

单行注释: // 可以嵌套

多行注释: /* */ 不可以嵌套

数据

静态数据

永久性的数据,一般存在硬盘

硬盘的访问速度很慢

动态数据

在程序运行过程中,动态产生的临时数据,一般存储在内存中.

断电就会消失.

内存访问非常快

数据类型

  1. 基本数据类型
  2. 指针类型
  3. 构造类型
  4. 空类型

常量

表示一些固定的数据

整形常量

10   -33

浮点常量

0.33f  0.055

字符常量

'a'  'dd'  '+' //字符那个用单引号

字符串常量

"fasdfa"  "343ed"  //字符串用双引号

0.0是小数

变量

一些经常改变的不确定数据,就用变量来表示,比如游戏积分

定义变量

  1. 使用之前必须定义
  2. 变量类型 变量名
  3. 不同的变量类型占用不同大小的存储空间.
  4. 第一次赋值是初始化

int 4个字节
char 1个字节

输出变量:

printf('%d',name);

占位符:

%d/$i   整形
%.4f 4为小数
%c   字符
%p  地址

变量在函数中的作用域

在函数里面:

从开始定义的那个地方开始,一直到函数结束.

函数在调用的是偶分配内存空间,函数结束的时候释放内存空间

变量在代码块中的作用域

代码块:

int main()
{
    {
        这就是一个代码块,在这里面定义的变量,只能在这里面用
    }
    
    //但是在这里定义的变量可以被代码块中使用
    int a = 10;
    {
        printf('d%',a);
    }
}

在代码块中使用变量,优先在自己的快里找,然后依次往上找

在函数外面定义的是全局变量,函数内的为局部变量

代码块用来提高性能的,可以在适当的时候释放变量,不会一直占用内存
及时回收不再使用的变量

变量的内存分析

变量在内存中占据多个字节:

char 1
int  4
float 4
double 8
  1. 内存的寻址从最大的开始,也就是从底部入栈

  2. 变量越先定义,内存的地址就越大.

  3. 与之对应的变量的地址是变量所占字节分配中的最小的一位

  4. 可以用&name取得name的地址. 输出用 &p

  5. 只要定义变量,就会分配地址,不管有没有值

scanf函数

用来接收用户的输入,是个阻断函数,必须完成后,下面的程序才会执行

只接收变量的地址

scanf("%d",&name);

多个参数以分隔符(任意)隔开,在输入的时候也必须以这个符号隔开
如果以空格隔开,在输入的时候用tab或者回车也可以

里面不能写\n,不然不会停止

运算符

+加法运算 还可以做正号
%去余 两边都是整数 正负数与左边的值有关

类型转换

自动转换

在运算或者赋值时,会自动将大类型转换为小类型

int a = 19.8;   //19

也可能将小类型提升为大类型:

double c = 10.9 +9;  //19.9000000

运算结果和参与运算的数据类型有关,两个整数运算结果必定是整数.

1/3 = 0;

强制转换

int a = (int)12.3;

符合运算

a += 1+2+3;  //等于 a = a+1+2+3;

b = ++a  先自增运算在赋值

b = a++  先赋值在自增运算

b = (++a) + (a++)  //b = 10 + 12

b = (a++) + (++a)  //b = 11 + 11

只有变量才允许自增自减

sizeop

获取一个变量或常量占据了多少字节

关系运算符

< <= > >= 的优先级高于 == !=

逻辑运算符

&& 逻辑与
|| 逻辑或
!逻辑非

三目运算符

int a = 10>5 ? 1:0;

流程控制

条件语句

如果要在if后面定义新的变量,必须用{}括起来

if()
int a = 10;   //报错
     
if()
printf("%d",a);  //对的

在switch语句中,case中也不能定义变量,不然又作用域不明确了,非要定义变量的话,必须用大括号括起来

switch(c){
    case '+':
        int sum = a + b;   //报错
        break;
}

但是可以加个大括号解决
switch(c){
    case '+':
        {
            int sum = a + b;   //报错
            break;
        }
}

循环

white循环

continue; 终止本次循环,继续下一次循环
break; 结束整个循环

如果没有大括号,就会执行挨着循环的那一条语句,注意变量作用域

do while 不管条件怎么样,先执行一次,再判断条件是否成立

注意事项

for(int i=0;i<5;i++){
    int i = 10;
    printf("%d",i);   //会输出5个 10 ,因为每一次循环结束,{}中的i就被释放了         
}
这是对的,因为括号里的作用域要比{}里面的大,可以看成for循环也是一个区域,而{}是form循环区域里的一起个小块

函数

格式:

返回值类型 函数名(参数){
    函数体
}

形参: 函数括号里的参数,可有可无

实参: 调用函数时传递的参数

函数体内不能定义和形参一样的变量

return 退出函数,返回一个具体的值给函数调用者 ,返回的类型必须和定义函数时的类型一样

也可以没有返回值,这样定义函数:

void test(){
    return;
}  这个函数也可以用return退出函数;

不写返回类型,默认返回int类型
就算写了返回类型,也可以不返回值

注意

函数不能重名

函数不能嵌套定义

函数要定义在main函数前面,因为程序是从上往下执行的

可以这样解决,把函数放在main的后面,但是要在main的前面写一个函数声明:

void test();

int main(){};

voed test(){}

函数可以重复声明

还可以在调用前声明:

init main(){
    void test();
    test();
}

编译的时候只检查语法
链接的时候会检查函数存不存在

include

系统自带的用<>
我们自己写的用""

多人开发

各自编译出来各自的之后,然后合并

cc a.o b.o c.o

还可以直接编译,但是不会提示报错信息

cc a.c b.c c.c

使用别人的函数,还在前面写出声明,我们可以引入.h文件

#include "func.h";

一般一个.c文件都会对应一个.h文件

main 和 printf

main函数中,return 0,正常退出;return -1,异常退出;

printf也有返回值,是输出占用字符的数量

中文占三个字符

进制

就是计数的方式

要了解四种进制: 10 2 8 16

默认就是10进制

二进制:

int num = 0b1100;  //前面带个0b就是二进制

八进制:

int num = 014;  //前面带个0就是八进制

十六进制:

int num = 0xc;  //前面带个0x就是十六进制

%d十进制输出, %o八进制输出,%x十六进制输出,%p输出地址,$f输出小数

变量在内存中的存储细节

任何数字在内存中存的都是二进制

一个int类型占四个字节,32bit

0000 0000 0000 0000 0000 0000 0000 0000 (一个字节八位,32位存4个字节)

进制转换

二进制转十进制

0000 0000 0000 0000 0000 0000 0000 1100 = 02的0次方 + 0的2的1次方 + 12的2次方 + 12的3次方

十进制转二进制

67 = 64 + 2 + 1
= 2的6次方 + 2的一次方 + 2的0次方
= 100011

二进制的取值范围

0-2的n次方 -1

int 2的31次方-1 因为第一位是符号位

最高位是0是正数

最高位是1是负数

类型说明符

int 4字节 %d (32位处理器2字节)
long 8字节 %ld (32位处理器4字节) 4354353425423523453425L
long long 8字节 %lld (32位处理器8字节) 34524352345243523452LL
short 2字节 %sd
sigend 有符号,正数,0,负数
unsigend 无符号 (Int会取值到32位)
同一个类型的修饰符不能同时使用,不同类型的可以同时使用

位运算

按位与: 相同为1 9&5

10101
01011
-----
00001

可以按位与一个1,返回最后一位的值,用来判断奇偶性,1为奇数,0为偶数

按位或: 任何一个为1就为1 9|8

101010
100101
------
101111

按位异或: 不同为1 9^8

101010
100101
------
001111

按位取反: 0变1,1变0 ~9

左移: 把整数的各二进制全部左移N位,高位丢弃,低位补0, 左移N位其实就是乘以2的N次方

有时候会把负数搞成正数

右移: 往右移动N位,低位丢失,高位补符号位,右移就是除以2的N次方

Mac高位补符号位,其他看编译器

char类型

字符在内存用是以ASCII码存的,占一个字节

A  65  内存中 0100 0001
B  66     内存中 0100 0010

列子:

char a = 'A' + 32;   // 结果是a
先进行运算  65 + 32 = 97   //就是a了

单引号只能用于一个单字节的字符

数组

构造类型: 由无数个基本类型组成的类型,构造类型

定义:

类型  名字[元素个数]
int arrs[5];
arrs[1] = 3;
arrs[2] = 4;

int arrs[5] = {1,2,3,4,5};

int arrs[5] = {[4]=9,[5]=9};

int arrs[] = {3,4,5};

int ages[bian]; //对

int ages[bian] = {3,3,4};//错  如果在定义的时候初始化,必须写常量

长度:

int count = sizeof(arrs)/sizeof(int);

数组放到函数里,可以省略参数:

void change(int array[]){}

如果在函数修改了数组元素,原数组也会改变,因为传进来的就是一个地址.也就是一个指针,指针永远是八个长度
所以无法获取到数组长度,在传递参数的时候,要把长度也传过去
基本类型是值传递

二维数组:

int arr[2][4]  = {
    {},
    {},
    ...
}

字符串

定义:

char name[] = "adsfdsa";  //默认最后会有一个\0

char name[7] = "adfads";

0的作用:

如果没有\0,字符串就不知道在哪结束,就会把其他的变量也都输出了,在内存中一直往下找,知道找到\0停止

长度

#include <string.h>
strlen("aaa");  //原理就是去说字符串的字节数,也就是指针地址,不包括\0

字符串数组:

name[3][10] = {"fdfd","dfa","adaa"}

指针变量

定义:

变量类型 *变量名
int *p = a;   //把a的地址存到了p里面

指针变量只能存储地址
指针能根据一个地址值,访问对应的存储空间,并进行值的修改
int表示P只能指向int类型的存储空间

    赋值   *p = 10;
    取值    *p;

指针使用注意

  1. 指针变量只能存地址
  2. 指针变量不能为空,必须要有确定指向的值,指针变量未经过初始化,不要拿来访问其他存储空间
  3. 定义指针时候的*没有任何意义,仅仅是一个象征,和后面的*不一样
  4. int *p; 代表 (int *)p,仅仅说明p是一个指针类型变量
  5. p = &a; 把某一个变量的地址给p , 后面再使用*p就是代表a的空间
  6. *p = 90; 代表的是给a的空间赋值, 而 *p是直接取出a空间的值
  7. char *cp; 字符指针

指向指针的指针

int a = 10;
int *p = &a;   //等于  int *p;  p = &a;
int **pp = &p;  //等于  int **pp; pp = &p;

此时, *p **p 本质上指向的都是变量a的存储空间,打印他们的地址,都是一样的
而每个指针变量,还有自己的地址,都是不一样的

指针类型

%zd,  sizeof(*cp);

任何指针都占用8个字节的存储空间

指针的的类型,决定了指针从地址开始取多少个字节

指针与数组

int ages[5] = {34,56,65,67,34};    
int *p;        
p = &ages[0];   //或者 p = &ages;
p++;   // 或者p+1;
printf("%d\n",*p);

把数组的一个元素的指针给p,
p存储的是指针类型,是一个地址,那么p+1,就是一个地址加上整形1,由于整形1占4个字节,那么p所指向的
地址也加四个字节,又因为整形占四个字节,所以p+1相当于指针p指向数组的元素地址向下移动了一个位置,
也就变成下一个元素了

指针变量的+1究竟加多少,取决与指针的类型:

int *  4
char * 1
double * 8

把数组给一个指针变量,就是把数组的第一个元素的地址给了指针变量

指针与字符串

char name[] = "it";
char *name2 = "it";   //把一个字符串给一个指针变量,参照数组,其实就是把字符串的第一个字符的地址给了指针变量.
printf("%c\n",*name2);  //i

printf("%s\n",name2);  //it

在OC中,用的比较多的就是指针操作字符串

数组定义的字符串是在栈中,它的值是随便可以更改的 (字符串变量)//使用场合,字符串的内容需要经常修改

而指针指向的字符串,是放在常量区,值不可以更改 (字符串常量) //经常用到的不需要修改的字符串

*name2 = "it";
*name3 = "it"; 
他们的地址是一样的

也就是说,放在常量区的会有一个缓存,我们下次再用的时候就不会再开辟一个存储空间了

指针数组: char *name[5] = {"aaaa","baaa","caaa","daaa","eaa"}

返回指针的函数

char *test()
{
    return "json";
}

指向函数的指针

//void代表函数的返回值类型
//固定写法 (*p)代表 这是一个函数指针
//()  指针指向的函数有没有形参
 void (*p)();   

p = test;  //把test函数的指针给P

(*p)();  //这样就可以直接代用函数  (*p)取出函数  ()调用函数

p();  //这样也可以调用函数   p已经代表函数的指针了

test();  //这样原始调用

其他数据类型

变量类型

局部变量:

定义在函数或者代码块中的变量,函数和代码块一执行完就销毁

全局变量:

定义在文件中,程序执行完销毁

结构体

构造类型:

数组  只能有一种数据类型

结构体:

可以有混合类型

结构体类型是不存在的,要我们自己定义:

    struct Person   //定义类型
    {
        int age;
        double height;
        char *name;
    }
    
    struct Person p = {20,1.55,"jack"};  //定义变量
    
    p.age = 30;  //还可以这样定义值
    
    struct Person p = {.age = 30,.height = 1.44,.name = "rose"};  //还可以这样定义

内存分析:

定义结构体并不会分配存储空间

一个结构体占的空间,是所有成员的和

对其算法,结构体所占的存储空间,一定是最大成员所占字节数的倍数

定义结构体的多种方式

上面说了一种,还有一种
struct Person   //定义类型
{
    int age;
    double height;
    char *name;
} p;   //定义变量;

不可重用的方式:
struct //定义类型
{
    int age;
    double height;
    char *name;
} p;   //定义变量;

类型的作用域

和变量的作用域差不多

结构体数组

struct Person pes[3] = {
    {},
    {},
    {}
};

指向结构体的指针

struct Person *p;
p = &Person;  //指针变量想保存谁,就把谁的地址拿过来存起来

第一种方式:
Person.age;

第二种方式:
(*p).age;

第三种方式:
p->age;

嵌套定义:

    struct Person   //定义类型
    {
        int age;
        double height;
        char *name;
        struct Date a;
        struct Date b;
    };

枚举类型

定义枚举类型

enum Season
{
    spring,   //0
    summer,    //1
    autumn,   //2
    winter    //3
}

定义枚举变量
enum Season s = spring;

总结

基本数据类型

int

long int, long: 8字节 %ld
short int, short: 2   %d %i
unsigned int, unsigned: 4  %zd
signed int, signed , int:4 %d  %i

float\double

float: 4 %f
double: 8  %f

char

1字节  %c  %d
char类型保存在内存中的是它的ASCII值

'A'  => 65

构造类型

数组

只能由同一种类型的数据组成
定义: 数据类型  数组名[元素个数]

结构体

可以由不同类型的数据组成
先定义结构, 再利用类型定义变量

指针类型

变量的定义

int *p

指针赋值

p = &age;

取值

*p;

修改值

*p = 22;

枚举类型

当一个变量只允许有几个固定取值时,可以使用枚举

其他

预处理指令

把代码翻译成0和1之前执行的指令(编译)

所有的预处理指令都是以#开头

位置随便写(也是有作用域的,从编写指令的那个位置开始,一直到文件结束)

宏定义

全局定义

#define COUNT 6  //也就是常量了,不过在编译之前就会执行,这样数组使用不会报错了

int age[COUNT] = {1,2,3,4,5,6};

失效

#undef COUNT;

带参数的宏定义

#define sum(v1,v2) (v1+v2)  //不加括号在用的时候会乱

条件编译

按条件去编译

#if (A == 5)
    ...
#elif (A == 6)
    ...
#endif
    ...

判断有没有定义宏

#if define(MAX)  //#ifndef MAX
    ...code...
#endif
    ...

文件包含

<>表示系统自带的文件, ""表示自定义的文件

不允许循环包含 A包含B,B包含A

解决多次包含只解析一次:

#ifndef A
    #define A 123  //可以不写值,定义一个空的宏
    int sum () {}
#endif

一般以文件名为宏名

关键字

typedef

int

typedef int MyInt;  //给int起个别名

char *

typedef char * String;  //给字符串指针一个别名
String name = "asdf";

结构体

typedef struct Student Mystu;  //给结构体定义一个别名
Mystu stu1,stu2;

还可以

typedef struct Student
{
    int age;
} Mystu;

还可以

typedef struct     //只能用Mystu访问了, 上面那种方法还可以使用原本的方式访问
{
    int age;
} Mystu;     

定义枚举类型,也可以参照结构体

指向函数的指针

typedef int (*MyPoint)(int, int);

Mypoint p = sum;
Mypoint p2 = minus;

原本是
int (*p)(int ,int) = sum;

指向结构体的指针

typedef struct Per * PersonPoint;

PersonPoint p = &p;

还可以用宏定义来代替
#define String char *; //但是只能单个使用,不能 String s1,s2;

static 和 extern

对函数和变量有效

对函数

外部函数:本文本和其他文本都可以访问,默认   extern省略
内部函数:只能被本文本访问,其他文件不能访问  static

两个文件都有同样名字的内部函数,是不冲突的

对变量

全局变量分两种:
    外部变量:本文件和其他文件都可以访问  默认  extern  ()
        不同文件中的同名变量,都代表同一个变量
    内部变量:只有本文件可以访问   static
    

static 定义了一个内部变量
extern 声明了一个外部变量

static 对局部变量的作用
    static修饰的局部变量(函数内) 函数结束变量不释放,一直到程序结束
            并没有改变局部变量的作用域
            多次执行函数,变量的值一直在变,所有的函数共享这个变量

递归

其他补充

指针的实质就是保存了一个地址

指针在64位系统中占8个字节

因为64的系统,内存地址有2的64次方,所以要8个字节去保存

在分配内存的时候,gcc编辑器会做一个优化,优先分配同一类型的变量,
而不是按照代码的编写顺序进行分配

内存结构,从上到下:

系统内存       系统使用

栈内存         保存的函数执行的状态,局部变量值 

可分配内存      由应用程序自由调配

堆内存         程序员自己分配

数据段         常量,全局变量,和静态变量

代码段         代码  a + b

array本质上是一个指针常量,地址固定
P指针是一个指针变量,位置可以随便移动 也可以用p[i]取值

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 1.编译程序(1)gcc xx.c,他会默认生成一个a.out的可执行文件,在a.out所在目录,执行./a.o...
    萌面大叔2阅读 1,258评论 0 1
  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,422评论 3 44
  • 上天说,该相遇的人总是会相遇的,初夏相信,所以她遇到了杜莫。 上天又说,所以的离别都是为了下次更好的相聚,初夏相信...
    白大狸阅读 689评论 2 7
  • 你是自由的,姑娘 时间的小船不会被巨浪掀翻 抬头的星空再亮 也会遇上名为“月亮”的风暴 猎户座的守护会很漫长 你要...
    租了五颗星阅读 276评论 3 1
  • 发呆 睡觉 吃饭
    初衷恙味阅读 276评论 0 0