学好一门语言就是要多看多练,C语言学习就是从最基本的hello world开始的。
/*
块注释,注释多行;注释内容不参与编译。
*/
// hello world 行注释
#include<stdio.h> //标准输入输出库,<>表示导入系统文件,""表示导入自定义文件
int main() //表示数据类型,返回为int
{
printf("Hello world!");
return 0;//函数返回值,和函数返回值对应
}
C语言简介
C语言有32个关键字,9种控制语句,34种运算符:
32个关键字 :不能用于其他用途;
auto | double | int | struct |
---|---|---|---|
break | else | long | switch |
case | enum | register | typedef |
char | extern | return | union |
const | float | short | unsigned |
continue | for | signed | void |
default | goto | sizeof | volatile |
do | if | static | while |
常量:整型常量,字符常量,浮点常量,枚举常量
整型常量:八进制:以0 开头,不包含8和9;十六进制:以0x或0X开头,包含a到f的字母。
字符常量:一个或多个字符构成,如‘x’。字符常量不包含’和换行符。
注:在没有显式说明情况下,enum类型中第一个枚举值为0,第二个为1,依次类推。
C程序编译
预处理:宏定义展开、头文件展开、条件编译等,同时会删除代码中注释。
编译:检查语法,将预处理后文件编译生成汇编文件。
汇编:将汇编文件生成目标文件(二进制文件)。
链接:把库链接到最终的可执行程序中去。
预处理:gcc -E test.c -o a.i
编译: gcc -S a.i -o a.s
汇编:gcc -c a.s -o a.o
链接:gcc a.o -o a
a.i
//原始代码
#include<stdio.h>
#define MAX 3
int main()
{
printf("%d",MAX);
return 0;
}
//预处理 a.i
# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 367 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4
# 410 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 411 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 368 "/usr/include/features.h" 2 3 4
# 391 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4
# 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4
# 392 "/usr/include/features.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4
...
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
typedef unsigned long int __u_long;
...
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 942 "/usr/include/stdio.h" 3 4
# 2 "test.c" 2
# 2 "test.c"
int main()
{
printf("%d",3);
return 0;
}
//编译 a.s
.file "test.c"
.section .rodata
.LC0:
.string "%d"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $3, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
//汇编,二进制文件
ELF\00\00\00\00\00\00\00\00\00\00>\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\A0\00\00\00\00\00\00\00\00\00\00@\00\00\00\00\00@\00
\00
\00UH\89\E5\BE\00\00\00\BF\00\00\00\00\B8\00\00\00\00\E8\00\00\00\00\B8\00\00\00\00]\C3%d\00\00GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609\00\00\00\00\00\00\00\00zR\00x\90\0
数据结构
字符串常量:
字符串是内存中一段连续的char空间,以‘\0’结尾。
字符串常量与字符常量不同:
‘a’ : 为字符常量;
“a” : 为字符串常量 ‘a’’\0’;
printf格式字符:
格式 | 类型 | 描述 |
---|---|---|
%d | int | 接收整数值并输出十进制整数 |
%f | float | 单精度浮点数 |
%x | unsigned int | 无符号16进制整数,abcd… |
%c | char | 字符型,ASCII |
%s | char * | 字符串。以’\0’结尾 |
%p | void * | 以十六进制形式输出指针 |
%% | % | 输出一个百分号 |
int main()
{
char ch='a';
printf("%c\n",ch);
ch=getchar(); //接收一个字符
putchar(ch); //输出一个字符
return 0;
}
运算符
运算符:+,-,* ,/,%(取模)
int main()
{
int a=10;
int b=5;
printf("%d",a%b); //值为0
return 0;
}
比较运算符:
==, !=, < ,>, <= , >=
逻辑运算符:
!,&&,||
程序流程结构
C基本支持的最基本三种结构:顺序结构,选择结构,循环结构。
选择结构:
if:
int main()
{
int score;
if(score>90) printf("PASS\n");
return 0;
三目运算符:
int main()
{
int a=10,b=20,c;
c=a>b?a:b;
printf("c=%d\n",c);
}
switch:
int main()
{
int score;
scanf("%d",&score);
switch(score/10)
case 1:
break;
case 2:
break;
default:
break;
}
循环结构:
while:
int main()
{
while(1)
{
}
}
do while:
for:
for(int i=0;i<10;i++)
跳转语句:
break: 跳出最近的内循环语句,执行后面的代码。
continue: 立即终止本次循环,并执行下一次循环。
基本声明:
int i;
int *b,*c,d; //d不是指针
初始化:
int j=15;
typedef:
推荐使用typedef而不是#define来创建新的类型名,后者无法正确的处理指针类型。
char *ptr_to_char; //把变量ptr_to_char声明为一个指向字符的指针。
typedef char *ptr_to_char; //把标识符ptr_to_char作为指向祖父的指针类型的新名字
ptr_to_char a; //声明a是一个指向字符的指针
常量使用:
int const a;
const int a;
两种写法都可以,都是把a声明为一个整数,值不能被修改。
int *pi; //一个指向整型的指针
int const *pci;// 一个指向整型常量的指针,可以修改指针的值,不能修改指向的值
int *const cpi;//一个指向整型的常量指针,指针为常量,无法修改,但可修改他指向的整型值
int const *const cpci;//无论指针还是指向的值都不允许修改
链接属性:
当组成一个程序的各个源文件分别被编译之后,所有的目标文件以及那些从一个或多个库中引用的函数链接在一起,形成可执行程序。
链接属性一共有3种:external,inernal,none。
关键字extern 和static用于在声明中修改标识符的链接属性。如果某个声明在正常情况下具有external链接属性,在它前面加上static关键字可以使它的链接属性变为internal。
static int b; //链接属性:internal
typedef char *a;
int b; //链接属性:extern
int c(int d) //c的链接属性:extern
{
int e;
int f(int g); //f的链接属性:extern
}
//其余标识符的链接属性均为:none
//extern属性,如果第二次声明,并不会更改第一次声明所指定的链接属性。
存储类型:
普通内存、运行时堆栈、硬件寄存器
int a=5; //a的链接属性为extern
extern int b;//extern属性,不是必须写
static int c;// internal属性,a,b,c的存储类型为静态,但并不存储与堆栈中。
int d(int e)//d具有extern,e不具有链接属性
{
int f=15;//f,b,g局部变量,不具有链接属性,f在每次调用时被初始化为15
register int b;//初始值时垃圾
static int g=20;//g的存储类型为静态,在程序开始时,被初始化为20,当函数调用时,不会重新初始化
extern int a;//声明不需要,在前面的作用域内
{
int e;//不具有链接属性
int a;//同名不能被访问
extern int h; //extern属性,必须使用extern
}
{
int x;
int e;
}
}
static int i()
{
}
操作符
a<<-5
对于有符号值,是采用逻辑移位还是算术移位取决于编译器。一个程序如果使用了有符号数的右移位操作,它就是不可移植的。
指针
未初始化和非法的指针
int *a; //指针没有初始化,没办法预测后面赋值语句把1存储在a所指向的内存位置
*a=1;
int a=12;
int *b=&a;
int **c=&b;
/*****/
a 12
b &a
*b a,12
c &b
*c b,&a
**c *b,a,12
++、- -优先级比*高
C语言实践
hello world学习
// hello world
//两个斜杠是行注释
/*这是块注释,可以注释若干行*/
//导入标准库,<>表示导入系统文件,""表示导入自定义文件
#include<stdio.h>
//int表示函数的返回值是一个整数
//main是程序的主函数
int main()
{
printf("Hello world!");
//return 表示函数结束
return 0;
}
打开文件学习:
int main()
{
Mat img;
//双斜杠表示一个斜杠,第一个是转义字符,一个正斜杠可以代替\\
img = imread("D:/temp/0001.jpg");
//img = imread("D:\\temp\\0001.jpg");
imshow("test", img);
waitKey(0);
return 0;
}
输入输出:
在高版本VS内使用scanf会报错C4996,原因:在标准C库内的函数,很多函数内部是不进行参数检测的,这些可能导致内存异常,所以微软改写了很多函数。
解决方法:添加:#define _CRT_SECURE_NO_WARNINGS可消除警告。
或者:#pragma warning(disable:4996)
控制流:
if(表达式)
语句;
else
语句;
switch(表达式){
case 常量表达式:语句序列
case 常量表达式:语句序列
default: 语句序列
}
while(表达式)
语句
for(表达式;表达式;表达式)
语句
堆栈操作:
堆栈是存储器中专用的缓冲区,用于暂存寄存器数据或地址指针,push/pop就用于堆栈的操作:
1、子程序调用,为了保护现场,把所要用的寄存器中的内容先暂时保存起来,在子程序调用结束之前,按照先进后出的原则,把数据恢复。
2、有时候,需要临时用一下某些寄存器,也可用一下,凭个人喜好;
堆栈操作指令
堆栈是一个先进后出的主存区域,位于堆栈段中,使用SS段寄存器记录器段地址。栈只有一个出口,即当前栈顶。栈顶是地址较小的一端(低端),它用堆栈指针寄存器SP指定。堆栈的两种基本操作,对应两条基本指令:
指针学习
int x=1,y=2,z[10];
int *ip; //ip是指向int类型的指针
ip=&x;//ip指向x
y=*ip;//y的值为1
*ip=0;//x的值为0
ip=&z[0];//ip指向z[0]
字符复制
//strcpy 数组法
void strcpy(char *s ,char *t)
{
int i=0;
while((s[i]=t[i])!='\0')
i++;
}
//指针法
void strcpy(char *s,char *t)
{
while((*s=*t)!='\0')
{
s++;
t++;
}
}
//优化
void strcpy(char *s,char *t)
{
while((*s++=*t++)!='\0');
}
结构体
struct point {
int x;
int y;
}
struct rect r,*rp=&r;
//以下是等价的
r.pt1.x
rp->pt1.x
(r.pt1).x
(rp->pt1).x
struct {
int len;
char *str;
} *p;
++p->len;//将增加len的值,相当与++(p->len)