赞
踩
友情提示:先关注收藏,再查看,13 万字保姆级 C 语言从入门到精通教程。
什么是计算机 ?
计算机的发明者是谁 ?
- 现在世界上***公认***的第一台现代电子计算机是1946年在美国宾夕法尼亚大学诞生的ENIAC(Electronic Numerical Integrator And Calculator)
计算机是一种电器, 所以计算机只能识别两种状态, 一种是通电一种是断电
正是因为如此, 最初ENIAC的程序是由很多开关和连接电线来完成的。但是这样导致***改动一次程序要花很长时间***(需要人工重新设置很多开关的状态和连接线)
为了提高效率,工程师们想能不能把程序和数据都放在存储器中, 数学家冯·诺依曼将这个思想以数学语言系统阐述,提出了存储程序计算机模型(这是所谓的冯·诺依曼机)
那利用数学语言如何表示计算机能够识别的通电和断电两种状态呢?
0和1更准确的是应该是高电平和低电平, 但是这个不用了解, 只需要知道计算机只能识别0和1以及存储的数据都是由0和1组成的即可。
计算机程序是为了告诉计算机"做某件事或解决某个问题"而用"***计算机语言***编写的命令集合(语句)
只要让计算机执行这个程序,计算机就会自动地、有条不紊地进行工作,计算机的一切操作都是由程序控制的,离开程序,计算机将一事无成
现实生活中你如何告诉别人如何做某件事或者解决某个问题?
10111000 00000001 00000000 00000101 00000001 00000000
MOV AX, 1 ADD AX, 1
1 + 1
“初,世间无语言,仅电路与连线。及大牛出,天地开,始有 FORTRAN、 LISP、ALGOL 随之, 乃有万种语”
当你想了解底层原理时,你才会发现后悔当初没有学习C语言
当你想学习一门新的语言时, 你才会发现后悔当初没有学习C语言
当你使用一些高级框架、甚至系统框架时发现提供的API都是C语言编写的, 你才发现后悔当初没有学习C语言
学好数理化,走遍天下都不拍
学好C语言,再多语言都不怕
学习本套课程之前 | 学习本套课程中 | 学习本套课程之后 |
---|---|---|
![]() | ![]() | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gHyaoC72-1623039894713)(https://upload-images.jianshu.io/upload_images/647982-c724f6cd01191121.png?imageMogr2/auto-orient/strip)] |
集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。
切记囫囵吞枣, 不要纠结里面的东西都是什么含义, 初学者安装成功就是一种成功
下载Qt Creator离线安装包:
以管理身份运行离线安装包
下一步,下一步,下一步,等待ing…
注意安装路径中最好不要出现中文
对于初学者而言全选是最简单的方式(重点!!!)
配置Qt Creator开发环境变量
你的安装路径\5.11.0\mingw53_32\bin
你的安装路径\Tools\mingw530_32\bin
手机有很多功能, “开机”,“关机”,“打电话”,“发短信”,"拍照"等等
手机中的每一个功能就相当于C语言程序中的一个程序段(函数)
众多功能中总有一个会被先执行,不可能多个功能一起执行
想使用手机必须先执行手机的开机功能
所以C语言程序也一样,由众多功能、众多程序段组成, 众多C语言程序段中总有一个会被先执行, 这个先执行的程序段我们称之为"主函数"
一个C语言程序由多个"函数"构成,每个函数有自己的功能
一个程序***有且只有一个主函数***
如果一个程序没有主函数,则这个程序不具备运行能力
程序运行时系统会***自动调用***主函数,而其它函数需要开发者***手动调用***
主函数有固定书写的格式和范写
int main() {
// insert code here...
return 0;
}
int call() {
return 0;
}
int main() {
call();
return 0;
}
int call(){
return 0;
}
int main(){
call();
printf();
return 0;
}
printf("hello world\n");
#include <stdio.h>
int call(){
return 0;
}
int main(){
call();
printf("hello world\n");
return 0;
}
int main(){
printf("hello world\n") // 如果没有分号编译时会报错
return 0;
}
int main(){
// 如果没有分号,多条语句合并到一行时, 系统不知道从什么地方到什么地方是一条完整语句
printf("hello world\n") return 0;
}
int main(){
printf("hello world\n"); // 这里的分号如果是中文的分号就会报错
return 0;
}
int main(){
return 0;
}
int main(){ // 编译时会报错, 重复定义
return 0;
}
int call(){ // 编译时报错, 因为只有call函数, 没有main函数
return 0;
}
int mian(){ // 编译时报错, 因为main函数的名称写错了,还是相当于没有main函数
return 0;
}
#include <stdio.h>
main(){ // 不会报错
printf("hello world\n");
return 0;
}
#include <stdio.h>
void main(){ // 不会报错
printf("hello world\n");
return 0;
}
int main(){ // 不会报错
printf("hello world\n");
}
#include <stdio.h>
int main(){
printf("hello world\n");
return 0;
}
Tips:
语法错误:编译器会直接报错
逻辑错误:没有语法错误,只不过运行结果不正确
*** ***
*********
*******
****
**
printf(" *** *** \n");
printf("*********\n");
printf(" *******\n");
printf(" ****\n");
printf(" **\n");
printf(" *** *** \n*********\n *******\n ****\n **\n");
int i = 0; while (1) { if (i % 2 == 0) { printf(" *** *** \n"); printf("*********\n"); printf(" *******\n"); printf(" ****\n"); printf(" **\n"); }else { printf("\n"); printf(" ** ** \n"); printf(" *******\n"); printf(" *****\n"); printf(" **\n"); } sleep(1); i++; system("cls"); }
_ooOoo_ o8888888o 88" . "88 (| -_- |) O\ = /O ____/`---'\____ . ' \\| |// `. / \\||| : |||// \ / _||||| -:- |||||- \ | | \\\ - /// | | | \_| ''\---/'' | | \ .-\__ `-` ___/-. / ___`. .' /--.--\ `. . __ ."" '< `.___\_<|>_/___.' >'"". | | : `- \`.;`\ _ /`;.`/ - ` : | | \ \ `-. \_ __\ /__ _/ .-` / / ======`-.____`-.___\_____/___.-`____.-'====== `=---=' ............................................. 佛祖保佑 有无BUG
━━━━━━神兽出没━━━━━━ ┏┓ ┏┓ ┏┛┻━━━━━━┛┻┓ ┃ ┃ ┃ ━ ┃ ┃ ┳┛ ┗┳ ┃ ┃ ┃ ┃ ┻ ┃ ┃ ┃ ┗━┓ ┏━┛Code is far away from bug with the animal protecting ┃ ┃ 神兽保佑,代码无bug ┃ ┃ ┃ ┗━━━┓ ┃ ┣┓ ┃ ┏━━┛┛ ┗┓┓┏━┳┓┏┛ ┃┫┫ ┃┫┫ ┗┻┛ ┗┻┛ ━━━━━━感觉萌萌哒━━━━━━
´´´´´´´´██´´´´´´´ ´´´´´´´████´´´´´´ ´´´´´████████´´´´ ´´`´███▒▒▒▒███´´´´´ ´´´███▒●▒▒●▒██´´´ ´´´███▒▒▒▒▒▒██´´´´´ ´´´███▒▒▒▒██´ 项目:第一个C语言程序 ´´██████▒▒███´´´´´ 语言: C语言 ´██████▒▒▒▒███´´ 编辑器: Qt Creator ██████▒▒▒▒▒▒███´´´´ 版本控制:git-github ´´▓▓▓▓▓▓▓▓▓▓▓▓▓▒´´ 代码风格:江哥style ´´▒▒▒▒▓▓▓▓▓▓▓▓▓▒´´´´´ ´.▒▒▒´´▓▓▓▓▓▓▓▓▒´´´´´ ´.▒▒´´´´▓▓▓▓▓▓▓▒ ..▒▒.´´´´▓▓▓▓▓▓▓▒ ´▒▒▒▒▒▒▒▒▒▒▒▒ ´´´´´´´´´███████´´´´´ ´´´´´´´´████████´´´´´´´ ´´´´´´´█████████´´´´´´ ´´´´´´██████████´´´´ 大部分人都在关注你飞的高不高,却没人在乎你飞的累不累,这就是现实! ´´´´´´██████████´´´ 我从不相信梦想,我,只,相,信,自,己! ´´´´´´´█████████´´ ´´´´´´´█████████´´´ ´´´´´´´´████████´´´´´ ________▒▒▒▒▒ _________▒▒▒▒ _________▒▒▒▒ ________▒▒_▒▒ _______▒▒__▒▒ _____ ▒▒___▒▒ _____▒▒___▒▒ ____▒▒____▒▒ ___▒▒_____▒▒ ███____ ▒▒ ████____███ █ _███_ _█_███ ——————————————————————————女神保佑,代码无bug——————————————————————
#include<stdio.h>
int main() {
printf("南哥带你装B带你飞");
return 0;
}
#include<iostream>
using namespace std;
int main() {
cout << "南哥带你装B带你飞" << endl;
return 0;
}
#import <Foundation/Foundation.h>
int main() {
NSLog(@"南哥带你装B带你飞");
return 0;
}
class Test
{
public static viod main()
{
system.out.println("南哥带你装B带你飞");
}
}
package main
import "fmt" //引入fmt库
func main() {
fmt.Println("南哥带你装B带你飞")
}
void printMap(char map[6][7] , int row, int col); int main(int argc, const char * argv[]) { char map[6][7] = { {'#', '#', '#', '#', '#', '#', '#'}, {'#', ' ', ' ', ' ', '#' ,' ', ' '}, {'#', 'R', ' ', '#', '#', ' ', '#'}, {'#', ' ', ' ', ' ', '#', ' ', '#'}, {'#', '#', ' ', ' ', ' ', ' ', '#'}, {'#', '#', '#', '#', '#', '#', '#'} }; int row = sizeof(map)/sizeof(map[0]); int col = sizeof(map[0])/ sizeof(map[0][0]); printMap(map, row, col); int pRow = 2; int pCol = 1; int endRow = 1; int endCol = 6; while ('R' != map[endRow][endCol]) { printf("亲, 请输入相应的操作\n"); printf("w(向上走) s(向下走) a(向左走) d(向右走)\n"); char run; run = getchar(); switch (run) { case 's': if ('#' != map[pRow + 1][pCol]) { map[pRow][pCol] = ' '; pRow++;//3 map[pRow][pCol] = 'R'; } break; case 'w': if ('#' != map[pRow - 1][pCol]) { map[pRow][pCol] = ' '; pRow--; map[pRow][pCol] = 'R'; } break; case 'a': if ('#' != map[pRow][pCol - 1]) { map[pRow][pCol] = ' '; pCol--; map[pRow][pCol] = 'R'; } break; case 'd': if ('#' != map[pRow][pCol + 1]) { map[pRow][pCol] = ' '; pCol++; map[pRow][pCol] = 'R'; } break; } printMap(map, row, col); } printf("你太牛X了\n"); printf("想挑战自己,请购买完整版本\n"); return 0; } void printMap(char map[6][7] , int row, int col) { system("cls"); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { printf("%c", map[i][j]); } printf("\n"); } }
/* R代表一个人 #代表一堵墙 // 0123456 ####### // 0 # # // 1 #R ## # // 2 # # # // 3 ## # // 4 ####### // 5 分析: >1.保存地图(二维数组) >2.输出地图 >3.操作R前进(控制小人行走) 3.1.接收用户输入(scanf/getchar) w(向上走) s(向下走) a(向左走) d(向右走) 3.2.判断用户的输入,控制小人行走 3.2.1.替换二维数组中保存的数据 ( 1.判断是否可以修改(如果不是#就可以修改) 2.修改现有位置为空白 3.修改下一步为R ) 3.3.输出修改后的二维数组 4.判断用户是否走出出口 */ // 声明打印地图方法 void printMap(char map[6][7] , int row, int col); int main(int argc, const char * argv[]) { // 1.定义二维数组保存迷宫地图 char map[6][7] = { {'#', '#', '#', '#', '#', '#', '#'}, {'#', ' ', ' ', ' ', '#' ,' ', ' '}, {'#', 'R', ' ', '#', '#', ' ', '#'}, {'#', ' ', ' ', ' ', '#', ' ', '#'}, {'#', '#', ' ', ' ', ' ', ' ', '#'}, {'#', '#', '#', '#', '#', '#', '#'} }; // 2.计算地图行数和列数 int row = sizeof(map)/sizeof(map[0]); int col = sizeof(map[0])/ sizeof(map[0][0]); // 3.输出地图 printMap(map, row, col); // 4.定义变量记录人物位置 int pRow = 2; int pCol = 1; // 5.定义变量记录出口的位置 int endRow = 1; int endCol = 6; // 6.控制人物行走 while ('R' != map[endRow][endCol]) { // 6.1提示用户如何控制人物行走 printf("亲, 请输入相应的操作\n"); printf("w(向上走) s(向下走) a(向左走) d(向右走)\n"); char run; run = getchar(); // 6.2根据用户输入控制人物行走 switch (run) { case 's': if ('#' != map[pRow + 1][pCol]) { map[pRow][pCol] = ' '; pRow++;//3 map[pRow][pCol] = 'R'; } break; case 'w': if ('#' != map[pRow - 1][pCol]) { map[pRow][pCol] = ' '; pRow--; map[pRow][pCol] = 'R'; } break; case 'a': if ('#' != map[pRow][pCol - 1]) { map[pRow][pCol] = ' '; pCol--; map[pRow][pCol] = 'R'; } break; case 'd': if ('#' != map[pRow][pCol + 1]) { map[pRow][pCol] = ' '; pCol++; map[pRow][pCol] = 'R'; } break; } // 6.3重新输出行走之后的地图 printMap(map, row, col); } printf("你太牛X了\n"); printf("想挑战自己,请购买完整版本\n"); return 0; } /** * @brief printMap * @param map 需要打印的二维数组 * @param row 二维数组的行数 * @param col 二维数组的列数 */ void printMap(char map[6][7] , int row, int col) { // 为了保证窗口的干净整洁, 每次打印都先清空上一次的打印 system("cls"); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { printf("%c", map[i][j]); } printf("\n"); } }
单行注释
多行注释
// 南哥 // it666.com
// /* 江哥 */
// 帅哥
/*
// 作者:LNJ
// 描述:第一个C语言程序作用:这是一个主函数,C程序的入口点
*/
/*
哈哈哈
/*嘻嘻嘻*/
呵呵呵
*/
/* R代表一个人 #代表一堵墙 // 0123456 ####### // 0 # # // 1 #R ## # // 2 # # # // 3 ## # // 4 ####### // 5 分析: >1.保存地图(二维数组) >2.输出地图 >3.操作R前进(控制小人行走) 3.1.接收用户输入(scanf/getchar) w(向上走) s(向下走) a(向左走) d(向右走) 3.2.判断用户的输入,控制小人行走 3.2.1.替换二维数组中保存的数据 ( 1.判断是否可以修改(如果不是#就可以修改) 2.修改现有位置为空白 3.修改下一步为R ) 3.3.输出修改后的二维数组 4.判断用户是否走出出口 */
// 2.计算地图行数和列数
int row = sizeof(map)/sizeof(map[0]);
int col = sizeof(map[0])/ sizeof(map[0][0]);
/** * @brief printMap * @param map 需要打印的二维数组 * @param row 二维数组的行数 * @param col 二维数组的列数 */ void printMap(char map[6][7] , int row, int col) { system("cls"); for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { printf("%c", map[i][j]); } printf("\n"); } }
// 1.定义二维数组保存迷宫地图 char map[6][7] = { {'#', '#', '#', '#', '#', '#', '#'}, {'#', ' ', ' ', ' ', '#' ,' ', ' '}, {'#', 'R', ' ', '#', '#', ' ', '#'}, {'#', ' ', ' ', ' ', '#', ' ', '#'}, {'#', '#', ' ', ' ', ' ', ' ', '#'}, {'#', '#', '#', '#', '#', '#', '#'} }; // 2.计算地图行数和列数 int row = sizeof(map)/sizeof(map[0]); int col = sizeof(map[0])/ sizeof(map[0][0]); // 3.输出地图 printMap(map, row, col); // 4.定义变量记录人物位置 int pRow = 2; int pCol = 1; // 5.定义变量记录出口的位置 int endRow = 1; int endCol = 6; // 6.控制人物行走 while ('R' != map[endRow][endCol]) { ... ... }
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|
char | short | int | long | float | double | if | else |
return | do | while | for | switch | case | break | continue |
default | goto | sizeof | auto | register | static | extern | unsigned |
signed | typedef | struct | enum | union | void | const | volatile |
这些不用专门去记住,用多了就会了。在编译器里都是有特殊颜色的。 我们用到时候会一个一个讲解这个些关键字怎么用,现在浏览下,有个印象就OK了
fromNo22 | from#22 | my_Boolean | my-Boolean | 2ndObj | GUI | lnj |
Mike2jack | 江哥 | _test | test!32 | haha(da)tt | jack_rose | jack&rose |
生活中无时无刻都在跟数据打交道
在我们使用计算机的过程中,也会接触到各种各样的数据
静态的数据
动态的数据
既然硬盘的存储空间这么大,为何不把所有的应用程序加载到硬盘中去执行呢?
1 B(Byte字节) = 8 bit(位)
// 00000000 就是一个字节
// 111111111 也是一个字节
// 10101010 也是一个字节
// 任意8个0和1的组合都是一个字节
1 KB(KByte) = 1024 B
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB
整型常量
实型常量
字符常量
字符串常量
自定义常量
常量类型练习
123 | 1.1F | 1.1 | .3 | ‘a’ | “a” | “李南江” |
int a;
float b;
char ch;
int a,b,c;
int value;
value = 998; // 赋值
int value; value = 998; // 初始化
int a = 10; int b = 4, c = 2;
int a, b = 10; //部分初始化
int c, d, e;
c = d = e =0;
int i = 10;
i = 20; // 修改变量的值
int a = 10;
int b = a; // 相当于把a中存储的10拷贝了一份给b
int a = 10, c = 11;
printf("a=%d, c=%d", a, c);
double height = 1.75;
char blood = 'A';
printf("height=%.2f, 血型是%c", height, blood);
int main(){
int i = 998; // 作用域开始
return 0;// 作用域结束
}
int main(){
{
int i = 998; // 作用域开始
}// 作用域结束
printf("i = %d\n", i); // 不能使用
return 0;
}
int main(){
{
{
int i = 998;// 作用域开始
}// 作用域结束
printf("i = %d\n", i); // 不能使用
}
return 0;
}
int i = 666;
int main(){
printf("i = %d\n", i); // 可以使用
return 0;
}// 作用域结束
int call(){
printf("i = %d\n", i); // 可以使用
return 0;
}
int main(){
int i = 998; // 作用域开始
int i = 666; // 报错, 重复定义
return 0;
}// 作用域结束
int i = 666;
int i = 998; // 报错, 重复定义
int main(){
return 0;
}
int i = 666;
int main(){
int i = 998; // 不会报错
return 0;
}
int main(){
int i = 998; // 不会报错
return 0;
}
int call(){
int i = 666; // 不会报错
return 0;
}
类型 | 16位编译器 | 32位编译器 | 64位编译器 |
---|---|---|---|
char | 1 | 1 | 1 |
int | 2 | 4 | 4 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
short | 2 | 2 | 2 |
long | 4 | 4 | 8 |
long long | 8 | 8 | 8 |
void* | 2 | 4 | 8 |
int main(){
int number;
int value;
number = 22;
value = 666;
}
#include <stdio.h>
int main(){
int number;
int value;
number = 22;
value = 666;
printf("&number = %p\n", &number); // 0060FEAC
printf("&value = %p\n", &value); // 0060FEA8
}
先不要着急, 刚开始接触C语言, 我先了解这么多就够了. 后面会再次更深入的讲解存储的各种细节。
printf("格式控制字符串",输出项列表 );
printf("a = %d, b = %d",a, b);
%[标志][输出宽度][.精度][长度]类型
printf("a = %类型", a);
类型 | 含义 |
---|---|
d | 有符号10进制整型 |
i | 有符号10进制整型 |
u | 无符号10进制整型 |
o | 无符号8进制整型 |
x | 无符号16进制整型 |
X | 无符号16进制整型 |
f | 单、双精度浮点数(默认保留6位小数) |
e / E | 以指数形式输出单、双精度浮点数 |
g / G | 以最短输出宽度,输出单、双精度浮点数 |
c | 字符 |
s | 字符串 |
p | 地址 |
#include <stdio.h> int main(){ int a = 10; int b = -10; float c = 6.6f; double d = 3.1415926; double e = 10.10; char f = 'a'; // 有符号整数(可以输出负数) printf("a = %d\n", a); // 10 printf("a = %i\n", a); // 10 // 无符号整数(不可以输出负数) printf("a = %u\n", a); // 10 printf("b = %u\n", b); // 429496786 // 无符号八进制整数(不可以输出负数) printf("a = %o\n", a); // 12 printf("b = %o\n", b); // 37777777766 // 无符号十六进制整数(不可以输出负数) printf("a = %x\n", a); // a printf("b = %x\n", b); // fffffff6 // 无符号十六进制整数(不可以输出负数) printf("a = %X\n", a); // A printf("b = %X\n", b); // FFFFFFF6 // 单、双精度浮点数(默认保留6位小数) printf("c = %f\n", c); // 6.600000 printf("d = %lf\n", d); // 3.141593 // 以指数形式输出单、双精度浮点数 printf("e = %e\n", e); // 1.010000e+001 printf("e = %E\n", e); // 1.010000E+001 // 以最短输出宽度,输出单、双精度浮点数 printf("e = %g\n", e); // 10.1 printf("e = %G\n", e); // 10.1 // 输出字符 printf("f = %c\n", f); // a }
printf("a = %[宽度]类型", a);
#include <stdio.h>
int main(){
// 实际位数小于指定宽度
int a = 1;
printf("a =|%d|\n", a); // |1|
printf("a =|%5d|\n", a); // | 1|
// 实际位数大于指定宽度
int b = 1234567;
printf("b =|%d|\n", b); // |1234567|
printf("b =|%5d|\n", b); // |1234567|
}
printf("a = %[标志][宽度]类型", a);
标志 | 含义 |
---|---|
- | 左对齐, 默认右对齐 |
+ | 当输出值为正数时,在输出值前面加上一个+号, 默认不显示 |
0 | 右对齐时, 用0填充宽度.(默认用空格填充) |
空格 | 输出值为正数时,在输出值前面加上空格, 为负数时加上负号 |
# | 对c、s、d、u类型无影响 |
# | 对o类型, 在输出时加前缀o |
# | 对x类型,在输出时加前缀0x |
#include <stdio.h> int main(){ int a = 1; int b = -1; // -号标志 printf("a =|%d|\n", a); // |1| printf("a =|%5d|\n", a); // | 1| printf("a =|%-5d|\n", a);// |1 | // +号标志 printf("a =|%d|\n", a); // |1| printf("a =|%+d|\n", a);// |+1| printf("b =|%d|\n", b); // |-1| printf("b =|%+d|\n", b);// |-1| // 0标志 printf("a =|%5d|\n", a); // | 1| printf("a =|%05d|\n", a); // |00001| // 空格标志 printf("a =|% d|\n", a); // | 1| printf("b =|% d|\n", b); // |-1| // #号 int c = 10; printf("c = %o\n", c); // 12 printf("c = %#o\n", c); // 012 printf("c = %x\n", c); // a printf("c = %#x\n", c); // 0xa }
printf("a = %[精度]类型", a);
#include <stdio.h>
int main(){
double a = 3.1415926;
printf("a = %.2f\n", a); // 3.14
}
printf("a = %.*f", a);
#include <stdio.h>
int main(){
double a = 3.1415926;
printf("a = %.*f", 2, a); // 3.14
}
#include <stdio.h>
int main(){
// 1234.567871093750000
float a = 1234.567890123456789;
// 1234.567890123456900
double b = 1234.567890123456789;
printf("a = %.15f\n", a); // 前8位数字是准确的, 后面的都不准确
printf("b = %.15f\n", b); // 前16位数字是准确的, 后面的都不准确
}
printf("a = %[长度]类型", a);
长度 | 修饰类型 | 含义 |
---|---|---|
hh | d、i、o、u、x | 输出char |
h | d、i、o、u、x | 输出 short int |
l | d、i、o、u、x | 输出 long int |
ll | d、i、o、u、x | 输出 long long int |
#include <stdio.h>
int main(){
char a = 'a';
short int b = 123;
int c = 123;
long int d = 123;
long long int e = 123;
printf("a = %hhd\n", a); // 97
printf("b = %hd\n", b); // 123
printf("c = %d\n", c); // 123
printf("d = %ld\n", d); // 123
printf("e = %lld\n", e); // 123
}
printf("%f%%", 3.1415);
#include <stdio.h>
int main(){
printf("%f%%", 3.1415); // 输出结果3.1415%
}
scanf("格式控制字符串", 地址列表);
scanf("%d", &num);
#include <stdio.h>
int main(){
int number;
scanf("%d", &number); // 接收一个整数
printf("number = %d\n", number);
}
#include <stdio.h>
int main(){
float num;
// 例如:输入 Tab 空格 回车 回车 Tab 空格 3.14 , 得到的结果还是3.14
scanf("%f", &num);
printf("num = %f\n", num);
}
#include <stdio.h>
int main(){
int number;
// 用户必须输入number = 数字 , 否则会得到一个意外的值
scanf("number = %d", &number);
printf("number = %d\n", number);
}
#include <stdio.h>
int main(){
int number;
scanf("%d", &number);
printf("number = %d\n", number);
int value;
scanf("%d", &value);
printf("value = %d\n", value);
}
#include <stdio.h>
int main(){
int number;
int value;
// 可以输入 数字 空格 数字, 或者 数字 回车 数字
scanf("%d%d", &number, &value);
printf("number = %d\n", number);
printf("value = %d\n", value);
}
#include <stdio.h>
int main(){
int number;
int value;
// 输入 数字,数字 即可
scanf("%d,%d", &number, &value);
printf("number = %d\n", number);
printf("value = %d\n", value);
}
#include <stdio.h>
int main(){
int number;
// 输入完毕之后按下回车无法结束输入
scanf("%d\n", &number);
printf("number = %d\n", number);
}
#include <stdio.h>
int main(){
int num1;
int num2;
char ch1;
scanf("%d%c%d", &num1, &ch1, &num2);
printf("num1 = %d, ch1 = %c, num2 = %d\n", num1, ch1, num2);
char ch2;
int num3;
scanf("%c%d",&ch2, &num3);
printf("ch2 = %c, num3 = %d\n", ch2, num3);
}
fflush(stdin);
setbuf(stdin, NULL);
#include <stdio.h>
int main(){
int num1;
int num2;
char ch1;
scanf("%d%c%d", &num1, &ch1, &num2);
printf("num1 = %d, ch1 = %c, num2 = %d\n", num1, ch1, num2);
//fflush(stdin); // 清空输入缓存区
setbuf(stdin, NULL); // 清空输入缓存区
char ch2;
int num3;
scanf("%c%d",&ch2, &num3);
printf("ch2 = %c, num3 = %d\n", ch2, num3);
}
#include <stdio.h>
int main(){
char ch = 'a';
putchar(ch); // 输出a
}
#include <stdio.h>
int main(){
char ch;
ch = getchar();// 获取一个字符
printf("ch = %c\n", ch);
}
和数学中的运算符一样, C语言中的运算符是告诉程序执行特定算术或逻辑操作的符号
什么是表达式
优先级 | 名称 | 符号 | 说明 |
---|---|---|---|
3 | 乘法运算符 | * | 双目运算符,具有左结合性 |
3 | 除法运算符 | / | 双目运算符,具有左结合性 |
3 | 求余运算符 (模运算符) | % | 双目运算符,具有左结合性 |
4 | 加法运算符 | + | 双目运算符,具有左结合性 |
4 | 减法运算符 | - | 双目运算符,具有左结合性 |
#include <stdio.h> int main(){ int a = 10; int b = 5; // 加法 int result = a + b; printf("%i\n", result); // 15 // 减法 result = a - b; printf("%i\n", result); // 5 // 乘法 result = a * b; printf("%i\n", result); // 50 // 除法 result = a / b; printf("%i\n", result); // 2 // 算术运算符的结合性和优先级 // 结合性: 左结合性, 从左至右 int c = 50; result = a + b + c; // 15 + c; 65; printf("%i\n", result); // 优先级: * / % 大于 + - result = a + b * c; // a + 250; 260; printf("%i\n", result); }
#include <stdio.h>
int main(){
// 整数除以整数, 结果还是整数
printf("%i\n", 10 / 3); // 3
// 参与运算的任何一个数是小数, 结果就是小数
printf("%f\n", 10 / 3.0); // 3.333333
}
#include <stdio.h> int main(){ // 10 / 3 商等于3, 余1 int result = 10 % 3; printf("%i\n", result); // 1 // 左边小于右边, 那么结果就是左边 result = 2 % 10; printf("%i\n", result); // 2 // 被除数是正数结果就是正数,被除数是负数结果就是负数 result = 10 % 3; printf("%i\n", result); // 1 result = -10 % 3; printf("%i\n", result); // -1 result = 10 % -3; printf("%i\n", result); // 1 }
优先级 | 名称 | 符号 | 说明 |
---|---|---|---|
14 | 赋值运算符 | = | 双目运算符,具有右结合性 |
14 | 除后赋值运算符 | /= | 双目运算符,具有右结合性 |
14 | 乘后赋值运算符 (模运算符) | *= | 双目运算符,具有右结合性 |
14 | 取模后赋值运算符 | %= | 双目运算符,具有右结合性 |
14 | 加后赋值运算符 | += | 双目运算符,具有右结合性 |
14 | 减后赋值运算符 | -= | 双目运算符,具有右结合性 |
#include <stdio.h>
int main(){
// 简单的赋值运算符 =
// 会将=右边的值赋值给左边
int a = 10;
printf("a = %i\n", a); // 10
}
#include <stdio.h> int main(){ // 复合赋值运算符 += -= *= /= %= // 将变量中的值取出之后进行对应的操作, 操作完毕之后再重新赋值给变量 int num1 = 10; // num1 = num1 + 1; num1 = 10 + 1; num1 = 11; num1 += 1; printf("num1 = %i\n", num1); // 11 int num2 = 10; // num2 = num2 - 1; num2 = 10 - 1; num2 = 9; num2 -= 1; printf("num2 = %i\n", num2); // 9 int num3 = 10; // num3 = num3 * 2; num3 = 10 * 2; num3 = 20; num3 *= 2; printf("num3 = %i\n", num3); // 20 int num4 = 10; // num4 = num4 / 2; num4 = 10 / 2; num4 = 5; num4 /= 2; printf("num4 = %i\n", num4); // 5 int num5 = 10; // num5 = num5 % 3; num5 = 10 % 3; num5 = 1; num5 %= 3; printf("num5 = %i\n", num5); // 1 }
#include <stdio.h>
int main(){
int number = 10;
// 赋值运算符优先级是14, 普通运算符优先级是3和4, 所以先计算普通运算符
// 普通运算符中乘法优先级是3, 加法是4, 所以先计算乘法
// number += 1 + 25; number += 26; number = number + 26; number = 36;
number += 1 + 5 * 5;
printf("number = %i\n", number); // 36
}
优先级 | 名称 | 符号 | 说明 |
---|---|---|---|
2 | 自增运算符(在后) | i++ | 单目运算符,具有左结合性 |
2 | 自增运算符(在前) | ++i | 单目运算符,具有右结合性 |
2 | 自减运算符(在后) | i– | 单目运算符,具有左结合性 |
2 | 自减运算符(在前) | –i | 单目运算符,具有右结合性 |
#include <stdio.h>
int main(){
int number = 10;
number++;
printf("number = %i\n", number); // 11
++number;
printf("number = %i\n", number); // 12
}
#include <stdio.h>
int main(){
int number = 10;
// ++在后, 先参与表达式运算, 再自增
// 表达式运算时为: 3 + 10;
int result = 3 + number++;
printf("result = %i\n", result); // 13
printf("number = %i\n", number); // 11
}
#include <stdio.h>
int main(){
int number = 10;
// ++在前, 先自增, 再参与表达式运算
// 表达式运算时为: 3 + 11;
int result = 3 + ++number;
printf("result = %i\n", result); // 14
printf("number = %i\n", number); // 11
}
#include <stdio.h>
int main(){
int number = 10;
// --在后, 先参与表达式运算, 再自减
// 表达式运算时为: 10 + 3;
int result = number-- + 3;
printf("result = %i\n", result); // 13
printf("number = %i\n", number); // 9
}
#include <stdio.h>
int main(){
int number = 10;
// --在前, 先自减, 再参与表达式运算
// 表达式运算时为: 9 + 3;
int result = --number + 3;
printf("result = %i\n", result); // 12
printf("number = %i\n", number); // 9
}
++(a+b); 5++;
int i = 10;
int b = i++; // 不推荐
或者
int b = ++i; // 不推荐
或者
int a = 10;
int b = ++a + a++; // 不推荐
int i = 10;
int b = i; // 推荐
i++;
或者;
i++;
int b = i; // 推荐
或者
int a = 10;
++a;
int b = a + a; // 推荐
a++;
同一个表达式中同一个变量自增或自减后如何运算
, 不同编译器得到结果也不同, 在企业开发中千万不要这样写 int a = 1;
// 下列代码利用Qt运行时6, 利用Xcode运行是5
// 但是无论如何, 最终a的值都是3
// 在C语言中这种代码没有意义, 不用深究也不要这样写
// 特点: 参与运算的是同一个变量, 参与运算时都做了自增自减操作, 并且在同一个表达式中
int b = ++a + ++a;
printf("b = %i\n", b);
sizeof可以用来计算一个变量或常量、数据类型所占的内存字节数
sizeof的几种形式
sizeof(10);
char c = 'a'; sizeof(c);
sizeof 10;
char c = 'a'; sizeof c;
sizeof(float);
如果是数据类型不能省略括号
sizeof面试题:
#include <stdio.h>
int main(){
int a = 10;
double b = 3.14;
// 由于sizeof的优先级比+号高, 所以会先计算sizeof(a);
// a是int类型, 所以占4个字节得到结果4
// 然后再利用计算结果和b相加, 4 + 3.14 = 7.14
double res = sizeof a+b;
printf("res = %lf\n", res); // 7.14
}
表达式1,表达式2,… …,表达式n;
int result = a+1,b=3*4;
#include <stdio.h>
int main(){
int a = 10, b = 20, c;
// ()优先级高于逗号运算符和赋值运算符, 所以先计算()中的内容
// c = (11, 21);
// ()中是一个逗号表达式, 结果是最后一个表达式的值, 所以计算结果为21
// 将逗号表达式的结果赋值给c, 所以c的结果是21
c = (a + 1, b + 1);
printf("c = %i\n", c); // 21
}
优先级 | 名称 | 符号 | 说明 |
---|---|---|---|
6 | 大于运算符 | > | 双目运算符,具有左结合性 |
6 | 小于运算符 | < | 双目运算符,具有左结合性 |
6 | 大于等于运算符 | >= | 双目运算符,具有左结合性 |
6 | 小于等于运算符 | <= | 双目运算符,具有左结合性 |
7 | 等于运算符 | == | 双目运算符,具有左结合性 |
7 | 不等于运算符 | != | 双目运算符,具有左结合性 |
#include <stdio.h> int main(){ int result = 10 > 5; printf("result = %i\n", result); // 1 result = 5 < 10; printf("result = %i\n", result); // 1 result = 5 > 10; printf("result = %i\n", result); // 0 result = 10 >= 10; printf("result = %i\n", result); // 1 result = 10 <= 10; printf("result = %i\n", result); // 1 result = 10 == 10; printf("result = %i\n", result); // 1 result = 10 != 9; printf("result = %i\n", result); // 1 }
#include <stdio.h>
int main(){
// == 优先级 小于 >, 所以先计算>
// result = 10 == 1; result = 0;
int result = 10 == 5 > 3;
printf("result = %i\n", result); // 0
}
#include <stdio.h>
int main(){
// == 和 != 优先级一样, 所以按照结合性
// 关系运算符是左结合性, 所以从左至右计算
// result = 0 != 3; result = 1;
int result = 10 == 5 != 3;
printf("result = %i\n", result); // 1
}
int result1 = 3 > 4 + 7
int result2 = (3>4) + 7
int result3 = 5 != 4 + 2 * 7 > 3 == 10
#include <stdio.h>
int main(){
float a = 0.1;
float b = a * 10 + 0.00000000001;
double c = 1.0 + + 0.00000000001;
printf("b = %f\n", b);
printf("c = %f\n", c);
int result = b == c;
printf("result = %i\n", result); // 0
}
优先级 | 名称 | 符号 | 说明 |
---|---|---|---|
2 | 逻辑非运算符 | ! | 单目运算符,具有右结合性 |
11 | 逻辑与运算符 | && | 双目运算符,具有左结合性 |
12 | 逻辑或运算符 | \|\| | 双目运算符,具有左结合性 |
! 条件A;
#include <stdio.h>
int main(){
// ()优先级高, 先计算()里面的内容
// 10==10为真, 所以result = !(1);
// !代表真变假, 假变真,所以结果是假0
int result = !(10 == 10);
printf("result = %i\n", result); // 0
}
条件A && 条件B;
#include <stdio.h>
int main(){
// 真 && 真
int result = (10 == 10) && (5 != 1);
printf("result = %i\n", result); // 1
// 假 && 真
result = (10 == 9) && (5 != 1);
printf("result = %i\n", result); // 0
// 真 && 假
result = (10 == 10) && (5 != 5);
printf("result = %i\n", result); // 0
// 假 && 假
result = (10 == 9) && (5 != 5);
printf("result = %i\n", result); // 0
}
#include <stdio.h>
int main(){
int a = 10;
int b = 20;
// 逻辑与, 前面为假, 不会继续执行后面
int result = (a == 9) && (++b);
printf("result = %i\n", result); // 1
printf("b = %i\n", b); // 20
}
条件A || 条件B;
#include <stdio.h>
int main(){
// 真 || 真
int result = (10 == 10) || (5 != 1);
printf("result = %i\n", result); // 1
// 假 || 真
result = (10 == 9) || (5 != 1);
printf("result = %i\n", result); // 1
// 真 || 假
result = (10 == 10) || (5 != 5);
printf("result = %i\n", result); // 1
// 假 || 假
result = (10 == 9) || (5 != 5);
printf("result = %i\n", result); // 0
}
#include <stdio.h>
int main(){
int a = 10;
int b = 20;
// 逻辑或, 前面为真, 不会继续执行后面
int result = (a == 10) || (++b);
printf("result = %i\n", result); // 1
printf("b = %i\n", b); // 20
}
int result = 3>5 || 2<4 && 6<1;
三目运算符,它需要3个数据或表达式构成条件表达式
格式: 表达式1?表达式2(结果A):表达式3(结果B)
考试及格 ? 及格 : 不及格;
求值规则:
示例: int a = 10; int b = 20; int max = (a > b) ? a : b; printf("max = %d", max); 输出结果: 20 等价于: int a = 10; int b = 20; int max = 0; if(a>b){ max=a; }else { max=b; } printf("max = %d", max);
#include <stdio.h>
int main(){
int a = 10;
int b = 5;
// 先计算 a > b
// 然后再根据计算结果判定返回a还是b
// 相当于int max= (a>b) ? a : b;
int max= a>b ? a : b;
printf("max = %i\n", max); // 10
}
#include <stdio.h> int main(){ int a = 10; int b = 5; int c = 20; int d = 10; // 结合性是从右至左, 所以会先计算:后面的内容 // int res = a>b?a:(c>d?c:d); // int res = a>b?a:(20>10?20:10); // int res = a>b?a:(20); // 然后再计算最终的结果 // int res = 10>5?10:(20); // int res = 10; int res = a>b?a:c>d?c:d; printf("res = %i\n", res); }
强制类型转换(显示转换) | 自动类型转换(隐式转换) |
---|---|
(需要转换的类型)(表达式) | 1.算数转换 2.赋值转换 |
// 将double转换为int
int a = (int)10.5;
// 当前表达式用1.0占用8个字节, 2占用4个字节
// 所以会先将整数类型2转换为double类型之后再计算
double b = 1.0 / 2;
// 赋值时左边是什么类型,就会自动将右边转换为什么类型再保存
int a = 10.6;
// 结果为0, 因为参与运算的都是整型
double a = (double)(1 / 2);
// 结果为0.5, 因为1被强制转换为了double类型, 2也会被自动提升为double类型
double b = (double)1 / 2;
#include <stdio.h>
int main(){
double d = 3.14;
int num = (int)d;
printf("num = %i\n", num); // 3
printf("d = %lf\n", d); // 3.140000
}
交换前
int a = 10; int b = 20;
交换后
int a = 20; int b = 10;
默认情况下程序运行后,系统会按书写顺序从上至下依次执行程序中的每一行代码。但是这并不能满足我们所有的开发需求, 为了方便我们控制程序的运行流程,C语言提供3种流程控制结构,不同的流程控制结构可以实现不同的运行流程。
这3种流程结构分别是顺序结构、选择结构、循环结构
顺序结构:
选择结构
循环结构
if(表达式) {
语句块1;
}
后续语句;
if(age >= 18) {
printf("开网卡\n");
}
printf("买烟\n");
if(表达式){
语句块1;
}else{
语句块2;
}
后续语句;
if(age > 18){
printf("开网卡\n");
}else{
printf("喊家长来开\n");
}
printf("买烟\n");
if(表达式1) {
语句块1;
}else if(表达式2){
语句块2;
}else if(表达式3){
语句块3;
}else{
语句块4;
}
后续语句;
if(age>40){
printf("给房卡");
}else if(age>25){
printf("给名片");
}else if(age>18){
printf("给网卡");
}else{
printf("给好人卡");
}
printf("买烟\n");
if(表达式1){
语句块1;
if(表达式2){
语句块2;
}
}else{
if(表达式3){
语句块3;
}else{
语句块4;
}
}
#include <stdio.h>
int main(){
if(0){
printf("执行了if");
}else{
printf("执行了else"); // 被执行
}
}
// 极其不推荐写法
int age = 17;
if (age >= 18)
printf("开网卡\n");
else
printf("喊家长来开\n");
#include <stdio.h>
int main(){
if(0)
if(1)
printf("A\n");
else // 和if(1)匹配
printf("B\n");
else // 和if(0)匹配, 因为if(1)已经被匹配过了
if (1)
printf("C\n"); // 输出C
else // 和if(1)匹配
printf("D\n");
}
#include <stdio.h>
int main(){
if(1)
int number = 10; // 系统会报错
printf("number = %i\n", number);
}
#include <stdio.h>
int main(){
if(0){
int number = 10;
}else
int value = 20; // 系统会报错
printf("value = %i\n", value);
}
// 因为if(10 > 2)后面有一个分号, 所以系统会认为if省略了大括号
// if省略大括号时只能管控紧随其后的那条语句, 所以只能管控分号
if(10 > 2);
{
printf("10 > 2");
}
// 输出结果: 10 > 2
#include <stdio.h>
int main(){
int a = 8;
// if(a = 10){// 错误写法, 但不会报错
if (10 == a){
printf("a的值是10\n");
}else{
printf("a的值不是10\n");
}
}
if练习
实现石头剪刀布
剪刀石头布游戏:
1)定义游戏规则
剪刀 干掉 布
石头 干掉 剪刀
布 干掉石头
2)显示玩家开始猜拳
3)接收玩家输入的内容
4)让电脑随机产生一种拳
5)判断比较
(1)玩家赢的情况(显示玩家赢了)
(2)电脑赢的情况(显示电脑赢了)
(3)平局(显示平局)
switch(表达式){
case 常量表达式1:
语句1;
break;
case 常量表达式2:
语句2;
break;
case 常量表达式n:
语句n;
break;
default:
语句n+1;
break;
}
#include <stdio.h> int main() { int num = 3; switch(num){ case 1: printf("星期一\n"); break; case 2: printf("星期二\n"); break; case 3: printf("星期三\n"); break; case 4: printf("星期四\n"); break; case 5: printf("星期五\n"); break; case 6: printf("星期六\n"); break; case 7: printf("星期日\n"); break; default: printf("回火星去\n"); break; } }
#include <stdio.h> int main() { switch(1.1){ // 报错 case 1: printf("星期一\n"); break; case 2: printf("星期二\n"); break; default: printf("回火星去\n"); break; } }
#include <stdio.h> int main() { int num = 3; switch(1){ case 1: printf("星期一\n"); break; case 'a': printf("星期二\n"); break; case num: // 报错 printf("星期三\n"); break; case 4.0: // 报错 printf("星期四\n"); break; default: printf("回火星去\n"); break; } }
#include <stdio.h>
int main() {
switch(1){
case 1: // 报错
printf("星期一\n");
break;
case 1: // 报错
printf("星期一\n");
break;
default:
printf("回火星去\n");
break;
}
}
#include <stdio.h> int main() { switch(1){ case 1:{ int num = 10; printf("num = %i\n", num); printf("星期一\n"); break; } case 2: printf("星期一\n"); break; default: printf("回火星去\n"); break; } }
#include <stdio.h> int main() { int num = 2; switch(num){ case 1: printf("星期一\n"); break; case 2: printf("星期二\n"); // 被输出 case 3: printf("星期三\n"); // 被输出 default: printf("回火星去\n"); // 被输出 break; } }
#include <stdio.h>
int main() {
switch(1){
case 1:
printf("星期一\n");
break;
case 2:
printf("星期一\n");
break;
}
}
#include <stdio.h>
int main() {
switch(3){
case 1:
printf("星期一\n");
break;
default:
printf("Other,,,\n");
break;
case 2:
printf("星期一\n");
break;
}
}
#include <stdio.h>
int main() {
int a = -1;
scanf("%d", &a);
if(a > 100){
printf("用户输入的数据大于100");
}else{
printf("用户输入的数据不大于100");
}
}
#include <stdio.h> int main() { int a = -1; scanf("%d", &a); // 挺(T)萌(M)的(D)搞不定啊 switch (a) { case 101: case 102: case 103: case 104: case 105: printf("大于\n"); break; default: printf("不大于\n"); break; } }
要求用户输入一个分数,根据输入的分数输出对应的等级
A 90~100
B 80~89
C 70~79
D 60~69
E 0~59
while ( 循环控制条件 ) {
循环体中的语句;
能够让循环结束的语句;
....
}
构成循环结构的几个条件
示例:
int count = 0;
while (count < 3) { // 循环控制条件
printf("发射子弹~哔哔哔哔\n"); // 需要反复执行的语句
count++; // 能够让循环结束的语句
}
#include <stdio.h>
int main(){
int count = 4;
// 1.判断循环控制条件是否为真,此时为假所以跳过循环语句
while (count < 3) {
printf("发射子弹~哔哔哔哔\n");
count++;
}
// 2.执行循环语句后面的代码, 打印"循环执行完毕"
printf("循环执行完毕\n");
}
#include <stdio.h> int main(){ int count = 0; // 1.判断循环控制条件是否为真,此时0 < 3为真 // 4.再次判断循环控制条件是否为真,此时1 < 3为真 // 7.再次判断循环控制条件是否为真,此时2 < 3为真 // 10.再次判断循环控制条件是否为真,此时3 < 3为假, 跳过循环语句 while (count < 3) { // 2.执行循环体中的代码, 打印"发子弹" // 5.执行循环体中的代码, 打印"发子弹" // 8.执行循环体中的代码, 打印"发子弹" printf("发射子弹~哔哔哔哔\n"); // 3.执行"能够让循环结束的语句" count = 1 // 6.执行"能够让循环结束的语句" count = 2 // 9.执行"能够让循环结束的语句" count = 3 count++; } // 11.执行循环语句后面的代码, 打印"循环执行完毕" printf("循环执行完毕\n"); }
#include <stdio.h>
int main(){
while (1) { // 死循环
printf("发射子弹~哔哔哔哔\n");
// 没有能够让循环结束的语句
}
}
#include <stdio.h>
int main(){
while (1) // 死循环
printf("发射子弹~哔哔哔哔\n");
// 没有能够让循环结束的语句
}
#include <stdio.h>
int main(){
while (1) // 死循环
int num = 10; // 报错
// 没有能够让循环结束的语句
}
#include <stdio.h>
int main(){
int count = 0;
while (count < 3);{ // 死循环
printf("发射子弹~哔哔哔哔\n");
count++;
}
}
// 死循环一般在操作系统级别的应用程序会比较多, 日常开发中很少用
while (1);
do {
循环体中的语句;
能够让循环结束的语句;
....
} while (循环控制条件 );
int count = 0;
do {
printf("发射子弹~哔哔哔哔\n");
count++;
}while(count < 10);
do-while循环执行流程
应用场景
#include<stdio.h>
int main()
{
int num = -1;
do{
printf("请输入密码,验证您的身份\n");
scanf("%d", &num);
}while(123456 != num);
printf("主人,您终于回来了\n");
}
for(初始化表达式;循环条件表达式;循环后的操作表达式) {
循环体中的语句;
}
for(int i = 0; i < 10; i++){
printf("发射子弹~哔哔哔哔\n");
}
for循环执行流程
for循环注意点:
for(;;);
for和while应用场景
int count = 0; // 初始化表达式
while (count < 10) { // 条件表达式
printf("发射子弹~哔哔哔哔 %i\n", count);
count++; // 循环后增量表达式
}
// 如果初始化表达式的值, 需要在循环之后使用, 那么就用while
printf("count = %i\n", count);
// 注意: 在for循环初始化表达式中定义的变量, 只能在for循环后面的{}中访问
// 所以: 如果初始化表达式的值, 不需要在循环之后使用, 那么就用for
// 因为如果初始化表达式的值, 在循环之后就不需要使用了 , 那么用while会导致性能问题
for (int count = 0; count < 10; count++) {
printf("发射子弹~哔哔哔哔 %i\n", count);
}
// printf("count = %i\n", count);
// 如果需要使用初始化表达式的值, 也可以将初始化表达式写到外面
int count = 0;
for (; count < 10; count++) {
printf("发射子弹~哔哔哔哔\n", count);
}
printf("count = %i\n", count);
C语言中提供了四大跳转语句, 分别是return、break、continue、goto
break:
应用场景:
break注意点:
if(1) {
break; // 会报错
}
while(1) {
while(2) {
break;// 只对while2有效, 不会影响while1
}
printf("while1循环体\n");
}
while(2){
break;
printf("打我啊!");// 执行不到
}
if(1) {
continue; // 会报错
}
#include <stdio.h>
int main(){
int num = 0;
// loop:是定义的标记
loop:if(num < 10){
printf("num = %d\n", num);
num++;
// goto loop代表跳转到标记的位置
goto loop;
}
}
#include <stdio.h>
int main(){
while (1) {
while(2){
goto lnj;
}
}
lnj:printf("跳过了所有循环");
}
while(条件表达式) {
while循环结构 or dowhile循环结构 or for循环结构
}
for(初始化表达式;循环条件表达式;循环后的操作表达式) {
while循环结构 or dowhile循环结构 or for循环结构
}
do {
while循环结构 or dowhile循环结构 or for循环结构
} while (循环控制条件 );
for (row=0; row<100; row++) {
// 低效率:长循环在最外层
for ( col=0; col<5; col++ ) {
sum = sum + a[row][col];
}
}
for (col=0; col<5; col++ ) {
// 高效率:长循环在最内层
for (row=0; row<100; row++) {
sum = sum + a[row][col];
}
}
好友列表1
好友1
好友2
好友列表2
好友1
好友2
好友列表3
好友1
好友2
for (int i = 0; i < 4; i++) {
printf("好友列表%d\n", i+1);
for (int j = 0; j < 4; j++) {
printf(" 角色%d\n", j);
}
}
****
****
****
// 3行4列
// 外循环控制行数
for (int i = 0; i < 3; i++) {
// 内循环控制列数
for (int j = 0; j < 4; j++) {
printf("*");
}
printf("\n");
}
*
**
***
****
*****
/*
最多打印5行
最多打印5列
每一行和每一列关系是什么? 列数<=行数
*/
for(int i = 0; i< 5; i++) {
for(int j = 0; j <= i; j++) {
printf("*");
}
printf("\n");
}
*****
****
***
**
*
for(int i = 0; i< 5; i++) {
for(int j = i; j < 5; j++) {
printf("*");
}
printf("\n");
}
1
12
123
for (int i = 0; i < 3; i++) {
for (int j = 0; j <= i; j++) {
printf("%d", j+1);
}
printf("\n");
}
1
22
333
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= i; j++) {
printf("%d", i);
}
printf("\n");
}
--*
-***
*****
for (int i = 0; i <= 5; i++) {
for (int j = 0; j < 5 - i; j++) {
printf("-");
}
for (int m = 0; m < 2*i+1; m++) {
printf("*");
}
printf("\n");
}
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
printf("%d * %d = %d \t", j, i, (j * i));
}
printf("\n");
}
定义函数的目的
自定义函数的书写格式
返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,…) {
函数体;
返回值;
}
int main(){
printf("hello world\n");
retrun 0;
}
void 函数名() {
函数体;
}
// 1.没有返回值/没有形参
// 如果一个函数不需要返回任何数据给调用者, 那么返回值类型就是void
void printRose() {
printf(" {@}\n");
printf(" |\n");
printf(" \\|/\n"); // 注意: \是一个特殊的符号(转意字符), 想输出\必须写两个斜线
printf(" |\n");
// 如果函数不需要返回数据给调用者, 那么函数中的return可以不写
}
返回值类型 函数名() {
函数体;
return 值;
}
int getMax() {
printf("请输入两个整数, 以逗号隔开, 以回车结束\n");
int number1, number2;
scanf("%i,%i", &number1, &number2);
int max = number1 > number2 ? number1 : number2;
return max;
}
类型 变量名,类型 变量2,......
void 函数名(参数类型 形式参数1,参数类型 形式参数2,…) {
函数体;
}
void printMax(int value1, int value2) {
int max = value1 > value2 ? value1 : value2;
printf("max = %i\n", max);
}
返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,…) {
函数体;
return 0;
}
int printMax(int value1, int value2) {
int max = value1 > value2 ? value1 : value2;
return max;
}
void test() {
}
void test() { // 报错
}
int max(int number1, int number2) // 形式参数
{
return number1 > number2 ? number1 : number2;
}
int main() {
int num = 99;
// 88, num, 22+44均能得到一个确定的值, 所以都可以作为实参
max(88, num, 22+44); // 实际参数
return 0;
}
int max(int number1, int number2) { // 形式参数
return number1 > number2 ? number1 : number2;
}
int main() {
// 函数需要2个形参, 但是我们只传递了一个实参, 所以报错
max(88); // 实际参数
return 0;
}
void change(double number1, double number2) {// 形式参数
// 输出结果: 10.000000, 20.000000
// 自动将实参转换为double类型后保存
printf("number1 = %f, number2 = %f", number1, number2);
}
int main() {
change(10, 20);
return 0;
}
void change(int number1, int number2) { // 形式参数
number1 = 250; // 不会影响实参
number2 = 222;
}
int main() {
int a = 88;
int b = 99;
change(a, b);
printf("a = %d, b = %d", a, b); // 输出结果: 88, 99
return 0;
}
max(int number1, int number2) {// 形式参数
return number1 > number2 ? number1 : number2;
}
int height() {
return 3.14;
}
int main() {
double temp = height();
printf("%lf", temp);// 输出结果: 3.000000
}
int max(int number1, int number2) {// 形式参数
return number1 > number2 ? number1 : number2;
printf("执行不到"); // 执行不到
return 250; // 执行不到
}
int max( int a, int b );
int max( int, int );
// 函数声明
void getMax(int v1, int v2);
int main(int argc, const char * argv[]) {
getMax(10, 20); // 调用函数
return 0;
}
// 函数实现
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
// 函数声明
void getMax(int v1, int v2);
void getMax(int v1, int v2);
void getMax(int v1, int v2); // 不会报错
int main(int argc, const char * argv[]) {
getMax(10, 20); // 调用函数
return 0;
}
// 函数实现
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
int main(int argc, const char * argv[]) {
void getMax(int v1, int v2); // 函数声明, 不会报错
getMax(10, 20); // 调用函数
return 0;
}
// 函数实现
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
// 函数实现
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
int main(int argc, const char * argv[]) {
getMax(10, 20); // 调用函数
return 0;
}
int main(int argc, const char * argv[]) {
int res = getMin(5, 3); // 不会报错
printf("result = %d\n", res );
return 0;
}
int getMin(int num1, int num2) {// 返回int, 不用声明
return num1 < num2 ? num1 : num2;
}
void function(int x){
function(x);
}
void getNumber(){
int number = -1;
while (number < 0) {
printf("请输入一个正数\n");
scanf("%d", &number);
}
printf("number = %d\n", number);
}
void getNumber2(){
int number = -1;
printf("请输入一个正数abc\n");
scanf("%d", &number);
if (number < 0) {
// 负数
getNumber2();
}else{
// 正数
printf("number = %d\n", number);
}
}
递归和循环区别
递归练习
什么是进制?
常见的进制
进制书写的格式和规律
练习
00011 0x001 0x7h4 10.98 0986 .089-109
+178 0b325 0b0010 0xffdc 96f 96.0f 96.oF -.003
01100100
索引从右至左, 从零开始
第0位: 0 * 2^0 = 0;
第1位: 0 * 2^1 = 0;
第2位: 1 * 2^2 = 4;
第3位: 0 * 2^3 = 0;
第4位: 0 * 2^4 = 0;
第5位: 1 * 2^5 = 32;
第6位: 1 * 2^6 = 64;
第7位: 0 * 2^7 = 0;
最终结果为: 0 + 0 + 4 + 0 + 0 + 32 + 64 + 0 = 100
从右至左每3位划分为8进制的1位, 不够前面补0
001 100 100
第0位: 100 等于十进制 4
第1位: 100 等于十进制 4
第2位: 001 等于十进制 1
最终结果: 144就是转换为8进制的值
从右至左每4位划分为16进制的1位, 不够前面补0
0110 0100
第0位: 0100 等于十进制 4
第1位: 0110 等于十进制 6
最终结果: 64就是转换为16进制的值
十进制 --> 十进制
12345 = 10000 + 2000 + 300 + 40 + 5
= (1 * 10 ^ 4) + (2 * 10 ^ 3) + (3 * 10 ^ 2) + (4 * 10 ^ 1) + (5 * 10 ^ 0)
= (1 * 10000) + (2 + 1000) + (3 * 100) + (4 * 10) + (5 * 1)
= 10000 + 2000 + 300 + 40 + 5
= 12345
规律:
其它进制转换为十进制的结果 = 系数 * 基数 ^ 索引 之和
系数: 每一位的值就是一个系数
基数: 从x进制转换到十进制, 那么x就是基数
索引: 从最低位以0开始, 递增的数
二进制 --> 十进制
543210
101101 = (1 * 2 ^ 5) + (0 * 2 ^ 4) + (1 * 2 ^ 3) + (1 * 2 ^ 2) + (0 * 2 ^ 1) + (1 * 2 ^ 0)
= 32 + 0 + 8 + 4 + 0 + 1
= 45
八进制 --> 十进制
016 = (0 * 8 ^ 2) + (1 * 8 ^ 1) + (6 * 8 ^ 0)
= 0 + 8 + 6
= 14
十六进制 --> 十进制
0x11f = (1 * 16 ^ 2) + (1 * 16 ^ 1) + (15 * 16 ^ 0)
= 256 + 16 + 15
= 287
基数
取余, 倒叙读取十进制 --> 二进制 100 --> 1100100 100 / 2 = 50 0 50 / 2 = 25 0 25 / 2 = 12 1 12 / 2 = 6 0 6 / 2 = 3 0 3 / 2 = 1 1 1 / 2 = 0 1 十进制 --> 八进制 100 --> 144 100 / 8 = 12 4 12 / 8 = 1 4 1 / 8 = 0 1 十进制 --> 十六进制 100 --> 64 100 / 16 = 6 4 6 / 16 = 0 6
// 整数部分(除2取余) 12 / 2 ------ 6 // 余0 / 2 ------ 3 // 余0 / 2 ------ 1 // 余1 / 2 ------ 0 // 余1 //12 --> 1100 // 小数部分(乘2取整数积) 0.125 * 2 ------ 0.25 //0 0.25 * 2 ------ 0.5 //0 0.5 * 2 ------ 1.0 //1 0.0 // 0.125 --> 0.001 // 12.8125 --> 1100.001
// 整数部分(乘以2的n次方, n从0开始)
0 * 2^0 = 0
0 * 2^1 = 0
1 * 2^2 = 4
1 * 2^3 = 8
// 1100 == 8 + 4 + 0 + 0 == 12
// 小数部分(乘以2的负n次方, n从0开始)
0 * (1/2) = 0
0 * (1/4) = 0
1 * (1/8) = 0.125
// .100 == 0 + 0 + 0.125 == 0.125
// 1100.001 --> 12.125
0.8125 * 2 -------- 1.625 // 1 0.625 * 2 -------- 1.25 // 1 0.25 * 2 -------- 0.5 // 0 * 2 -------- 1.0 // 1 0.0 // 0. 8125 --> 0.1101
1*(1/2) = 0.5
1*(1/4)=0.25
0*(1/8)=0
1*(1/16)=0.0625
//0.1101 --> 0.5 + 0.25 + 0 + 0.0625 == 0.8125
0000 0000 0000 0000 0000 0000 0000 1100
0000 0000 0000 0000 0000 0000 0000 1100
0000 0000 0000 0000 0000 0000 0000 1100
除了符号位
以外的其它位取反 0000 0000 0000 0000 0000 0000 0000 1100 // 12二进制
1000 0000 0000 0000 0000 0000 0000 1100 // -12原码
1111 1111 1111 1111 1111 1111 1111 0011 // -12反码
1111 1111 1111 1111 1111 1111 1111 0100 // -12补码
1111 1111 1111 1111 1111 1111 1111 0100 // -12补码
1111 1111 1111 1111 1111 1111 1111 0011 // -12反码
1000 0000 0000 0000 0000 0000 0000 1100 // -12原码
// 1 + 1
0000 0000 0000 0000 0000 0000 0000 0001 // 1原码
+0000 0000 0000 0000 0000 0000 0000 0001 // 1原码
---------------------------------------
0000 0000 0000 0000 0000 0000 0000 0010 == 2
// 1 - 1; 1 + (-1);
0000 0000 0000 0000 0000 0000 0000 0001 // 1原码
+1000 0000 0000 0000 0000 0000 0000 0001 // -1原码
---------------------------------------
1000 0000 0000 0000 0000 0000 0000 0010 == -2
// 1 - 1; 1 + (-1);
0000 0000 0000 0000 0000 0000 0000 0001 // 1反码
1111 1111 1111 1111 1111 1111 1111 1110 // -1反码
---------------------------------------
1111 1111 1111 1111 1111 1111 1111 1111 // 计算结果反码
1000 0000 0000 0000 0000 0000 0000 0000 // 计算结果原码 == -0
// 1 - 1; 1 + (-1);
0000 0000 0000 0000 0000 0000 0000 0001 // 1补码
1111 1111 1111 1111 1111 1111 1111 1111 // -1补码
---------------------------------------
10000 0000 0000 0000 0000 0000 0000 0000 // 计算结果补码
0000 0000 0000 0000 0000 0000 0000 0000 // == 0
符号 | 名称 | 运算结果 |
---|---|---|
& | 按位与 | 同1为1 |
| | 按位或 | 有1为1 |
^ | 按位异或 | 不同为1 |
~ | 按位取反 | 0变1,1变0 |
<< | 按位左移 | 乘以2的n次方 |
>> | 按位右移 | 除以2的n次方 |
9&5 = 1
1001
&0101
------
0001
9|5 = 13
1001
|0101
------
1101
9^5 = 12
1001
^0101
------
1100
~9 =-10
0000 0000 0000 0000 0000 1001 // 取反前
1111 1111 1111 1111 1111 0110 // 取反后
// 根据负数补码得出结果
1111 1111 1111 1111 1111 0110 // 补码
1111 1111 1111 1111 1111 0101 // 反码
1000 0000 0000 0000 0000 1010 // 源码 == -10
偶数: 的二进制是以0结尾 8 -> 1000 10 -> 1010 奇数: 的二进制是以1结尾 9 -> 1001 11 -> 1011 任何数和1进行&操作,得到这个数的最低位 1000 &0001 ----- 0000 // 结果为0, 代表是偶数 1011 &0001 ----- 0001 // 结果为1, 代表是奇数
enum Unix {
S_IRUSR = 256,// 100000000 用户可读
S_IWUSR = 128,// 10000000 用户可写
S_IXUSR = 64,// 1000000 用户可执行
S_IRGRP = 32,// 100000 组可读
S_IWGRP = 16,// 10000 组可写
S_IXGRP = 8,// 1000 组可执行
S_IROTH = 4,// 100 其它可读
S_IWOTH = 2,// 10 其它可写
S_IXOTH = 1 // 1 其它可执行
};
// 假设设置用户权限为可读可写
printf("%d\n", S_IRUSR | S_IWUSR); // 384 // 110000000
a = a^b;
b = b^a;
a = a^b;
2<<1; //相当于 2 *= 2 // 4
0010
<<0100
2<<2; //相当于 2 *= 2^2; // 8
0010
<<1000
2>>1; //相当于 2 /= 2 // 1
0010
>>0001
4>>2; //相当于 4 /= 2^2 // 1
0100
>>0001
#include <stdio.h>
void printBinary(int num);
int main(int argc, const char * argv[]) {
printBinary(13);
}
void printBinary(int num){
int len = sizeof(int)*8;
int temp;
for (int i=0; i<len; i++) {
temp = num; //每次都在原数的基础上进行移位运算
temp = temp>>(31-i); //每次移动的位数
int t = temp&1; //取出最后一位
if(i!=0&&i%4==0)printf(" "); printf("%d",t);
}
}
- 地址总线: 地址总线宽度决定了CPU可以访问的物理地址空间(寻址能力)
- 例如: 地址总线的宽度是1位, 那么表示可以访问 0 和 1的内存
- 例如: 地址总线的位数是2位, 那么表示可以访问 00、01、10、11的内存
- 数据总线: 数据总线的位数决定CPU单次通信能交换的信息数量
- 例如: 数据总线:的宽度是1位, 那么一次可以传输1位二进制数据
- 例如: 地址总线的位数是2位,那么一次可以传输2位二进制数据
- 控制总线: 用来传送各种控制信号
写入流程
读取流程
变量的存储原则
10的二进制: 0b00000000 00000000 00000000 00001010
高字节← →低字节
字符 | 意义 |
---|---|
\b | 退格(BS)当前位置向后回退一个字符 |
\r | 回车(CR),将当前位置移至本行开头 |
\n | 换行(LF),将当前位置移至下一行开头 |
\t | 水平制表(HT),跳到下一个 TAB 位置 |
\0 | 用于表示字符串的结束标记 |
\ | 代表一个反斜线字符 \ |
\" | 代表一个双引号字符" |
\’ | 代表一个单引号字符’ |
char ch1 = 'a';
printf("%i\n", ch1); // 97
char ch2 = 97;
printf("%c\n", ch2); // a
char c = '我'; // 错误写法
char ch = 'ab'; // 错误写法
char ch1 = '6'; // 存储的是ASCII码 64
char ch2 = 6; // 存储的是数字 6
#include <stdio.h> int main() { // char占1个字节, char的取值范围 -2^7~2^7 char num = 129; printf("size = %i\n", sizeof(num)); // 1 printf("num = %i\n", num); // -127 // short int 占2个字节, short int的取值范围 -2^15~2^15-1 short int num1 = 32769;// -32767 printf("size = %i\n", sizeof(num1)); // 2 printf("num1 = %hi\n", num1); // int占4个字节, int的取值范围 -2^31~2^31-1 int num2 = 12345678901; printf("size = %i\n", sizeof(num2)); // 4 printf("num2 = %i\n", num2); // long在32位占4个字节, 在64位占8个字节 long int num3 = 12345678901; printf("size = %i\n", sizeof(num3)); // 4或8 printf("num3 = %ld\n", num3); // long在32位占8个字节, 在64位占8个字节 -2^63~2^63-1 long long int num4 = 12345678901; printf("size = %i\n", sizeof(num4)); // 8 printf("num4 = %lld\n", num4); // 由于short/long/long long一般都是用于修饰int, 所以int可以省略 short num5 = 123; printf("num5 = %lld\n", num5); long num6 = 123; printf("num6 = %lld\n", num6); long long num7 = 123; printf("num7 = %lld\n", num7); return 0; }
#include <stdio.h> int main() { // 1.默认情况下所有类型都是由符号的 int num1 = 9; int num2 = -9; int num3 = 0; printf("num1 = %i\n", num1); printf("num2 = %i\n", num2); printf("num3 = %i\n", num3); // 2.signed用于明确说明, 当前保存的数据可以是有符号的, 一般情况下很少使用 signed int num4 = 9; signed int num5 = -9; signed int num6 = 0; printf("num4 = %i\n", num4); printf("num5 = %i\n", num5); printf("num6 = %i\n", num6); // signed也可以省略数据类型, 但是不推荐这样编写 signed num7 = 9; printf("num7 = %i\n", num7); // 3.unsigned用于明确说明, 当前不能保存有符号的值, 只能保存0和正数 // 应用场景: 保存银行存款,学生分数等不能是负数的情况 unsigned int num8 = -9; unsigned int num9 = 0; unsigned int num10 = 9; // 注意: 不看怎么存只看怎么取 printf("num8 = %u\n", num8); printf("num9 = %u\n", num9); printf("num10 = %u\n", num10); return 0; }
signed short int num1 = 666;
signed unsigned int num2 = 666; // 报错
数组,从字面上看,就是一组数据的意思,没错,数组就是用来存储一组数据的
数组的几个名词
相同数据类型
数据的有序
的集合数组的应用场景
#include <stdio.h> int main(int argc, const char * argv[]) { /* // 需求: 保存2个人的分数 int score1 = 99; int score2 = 60; // 需求: 保存全班同学的分数(130人) int score3 = 78; int score4 = 68; ... int score130 = 88; */ // 数组: 如果需要保存`一组``相同类型`的数据, 就可以定义一个数组来保存 // 只要定义好一个数组, 数组内部会给每一块小的存储空间一个编号, 这个编号我们称之为 索引, 索引从0开始 // 1.定义一个可以保存3个int类型的数组 int scores[3]; // 2.通过数组的下标往数组中存放数据 scores[0] = 998; scores[1] = 123; scores[2] = 567; // 3.通过数组的下标从数组中取出存放的数据 printf("%i\n", scores[0]); printf("%i\n", scores[1]); printf("%i\n", scores[2]); return 0; }
// int 元素类型
// ages 数组名称
// [10] 元素个数
int ages[10];
int ages[3] = {4, 6, 9};
int nums[] = {1,2,3,5,6};
int nums[10] = {1,2};
int nums[5] = {[4] = 3,[1] = 2};
int nums[] = {[4] = 3};
int nums[3];
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
int nums[5];
printf("%d\n", nums[0]);
printf("%d\n", nums[1]);
printf("%d\n", nums[2]);
printf("%d\n", nums[3]);
printf("%d\n", nums[4]);
输出结果:
0
0
1606416312
0
1606416414
int ages[3];
ages = {4, 6, 9}; // 报错
// 找到下标为0的元素, 赋值为10
ages[0]=10;
// 取出下标为2的元素保存的值
int a = ages[2];
printf("a = %d", a);
int ages[4] = {19, 22, 33, 13};
for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
int ages[4] = {19, 22, 33, 13};
int length = sizeof(ages)/sizeof(int);
printf("length = %d", length);
输出结果: 4
int ages[4] = {19, 22, 33, 13};
for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
int ages[4] = {19, 22, 33, 13};
for (int i = 3; i >=0; i--) {
printf("ages[%d] = %d\n", i, ages[i]);
}
存储方式:
示例
#include <stdio.h> int main() { int num = 9; char cs[] = {'l','n','j'}; printf("cs = %p\n", &cs); // cs = 0060FEA9 printf("cs[0] = %p\n", &cs[0]); // cs[0] = 0060FEA9 printf("cs[1] = %p\n", &cs[1]); // cs[1] = 0060FEAA printf("cs[2] = %p\n", &cs[2]); // cs[2] = 0060FEAB int nums[] = {2, 6}; printf("nums = %p\n", &nums); // nums = 0060FEA0 printf("nums[0] = %p\n", &nums[0]);// nums[0] = 0060FEA0 printf("nums[1] = %p\n", &nums[1]);// nums[1] = 0060FEA4 return 0; }
- 注意:字符在内存中是以对应ASCII码值的二进制形式存储的,而非上述的形式。
char cs1[2] = {1, 2};
char cs2[3] = {3, 4, 5};
cs2[3] = 88; // 注意:这句访问到了不属于cs1的内存
printf("cs1[0] = %d\n", cs1[0] );
输出结果: 88
为什么上述会输出88, 自己按照"数组内部存储细节"画图脑补
int ages4['A'] = {19, 22, 33};
printf("ages4[0] = %d\n", ages4[0]);
int ages5[5 + 5] = {19, 22, 33};
printf("ages5[0] = %d\n", ages5[0]);
int ages5['A' + 5] = {19, 22, 33};
printf("ages5[0] = %d\n", ages5[0]);
// 没有指定元素个数,错误 int a[]; // []中不能放变量 int number = 10; int ages[number]; // 老版本的C语言规范不支持 printf("%d\n", ages[4]); int number = 10; int ages2[number] = {19, 22, 33} // 直接报错 // 只能在定义数组的时候进行一次性(全部赋值)的初始化 int ages3[5]; ages10 = {19, 22, 33}; // 一个长度为n的数组,最大下标为n-1, 下标范围:0~n-1 int ages4[4] = {19, 22, 33} ages4[8]; // 数组角标越界
void change(int val)// int val = number
{
val = 55;
}
int main(int argc, const char * argv[])
{
int ages[3] = {1, 5, 8};
printf("ages[0] = %d", ages[0]);// 1
change(ages[0]);
printf("ages[0] = %d", ages[0]);// 1
}
- 用数组元素作函数参数不要求形参也必须是数组元素
void change2(int array[3])// int array = 0ffd1
{
array[0] = 88;
}
int main(int argc, const char * argv[])
{
int ages[3] = {1, 5, 8};
printf("ages[0] = %d", ages[0]);// 1
change(ages);
printf("ages[0] = %d", ages[0]);// 88
}
void change(int array[])
{
array[0] = 88;
}
void prtArray(double array[3]) // 错误写法
{
for (int i = 0; i < 3; i++) {
printf("array[%d], %f", i, array[i]);
}
}
int main(int argc, const char * argv[])
{
int ages[3] = {1, 5, 8};
prtArray(ages[0]);
}
void printArray(int array[])
{
printf("printArray size = %lu\n", sizeof(array)); // 8
int length = sizeof(array)/ sizeof(int); // 2
printf("length = %d", length);
}
计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序
时,快于任何比较排序算法。
排序思路:
简单代码实现:
int main() { // 待排序数组 int nums[5] = {3, 1, 2, 0, 3}; // 用于排序数组 int newNums[4] = {0}; // 计算待排序数组长度 int len = sizeof(nums) / sizeof(nums[0]); // 遍历待排序数组 for(int i = 0; i < len; i++){ // 取出待排序数组当前值 int index = nums[i]; // 将待排序数组当前值作为排序数组索引 // 将用于排序数组对应索引原有值+1 newNums[index] = newNums[index] +1; } // 计算待排序数组长度 int len2 = sizeof(newNums) / sizeof(newNums[0]); // 输出排序数组索引, 就是排序之后结果 for(int i = 0; i < len2; i++){ for(int j = 0; j < newNums[i]; j++){ printf("%i\n", i); } } /* // 计算待排序数组长度 int len2 = sizeof(newNums) / sizeof(newNums[0]); // 还原排序结果到待排序数组 for(int i = 0; i < len2; i++){ int index = 0; for(int i = 0; i < len; i++){ for(int j = 0; j < newNums[i]; j++){ nums[index++] = i; } } } */ return 0; }
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾。以此类推,直到所有元素均排序完毕。
排序思路:
代码实现:
// 选择排序 void selectSort(int numbers[], int length) { // 外循环为什么要-1? // 最后一位不用比较, 也没有下一位和它比较, 否则会出现错误访问 for (int i = 0; i < length; i++) { for (int j = i; j < length - 1; j++) { // 1.用当前元素和后续所有元素比较 if (numbers[i] < numbers[j + 1]) { // 2.一旦发现小于就交换位置 swapEle(numbers, i, j + 1); } } } } // 交换两个元素的值, i/j需要交换的索引 void swapEle(int array[], int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; }
// 冒泡排序 void bubbleSort(int numbers[], int length) { for (int i = 0; i < length; i++) { // -1防止`角标越界`: 访问到了不属于自己的索引 for (int j = 0; j < length - i - 1; j++) { // 1.用当前元素和相邻元素比较 if (numbers[j] < numbers[j + 1]) { // 2.一旦发现小于就交换位置 swapEle(numbers, j, j + 1); } } } } // 交换两个元素的值, i/j需要交换的索引 void swapEle(int array[], int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; }
int main() { // 待排序数组 int nums[5] = {3, 1, 2, 0, 3}; // 0.计算待排序数组长度 int len = sizeof(nums) / sizeof(nums[0]); // 1.从第一个元素开始依次取出所有用于比较元素 for (int i = 1; i < len; i++) { // 2.取出用于比较元素 int temp = nums[i]; int j = i; while(j > 0){ // 3.判断元素是否小于前一个元素 if(temp < nums[j - 1]){ // 4.让前一个元素向后移动一位 nums[j] = nums[j - 1]; }else{ break; } j--; } // 5.将元素插入到空出来的位置 nums[j] = temp; } }
int main() { // 待排序数组 int nums[5] = {3, 1, 2, 0, 3}; // 0.计算待排序数组长度 int len = sizeof(nums) / sizeof(nums[0]); // 1.从第一个元素开始依次取出所有用于比较元素 for (int i = 1; i < len; i++) { // 2.遍历取出前面元素进行比较 for(int j = i; j > 0; j--) { // 3.如果前面一个元素大于当前元素,就交换位置 if(nums[j-1] > nums[j]){ int temp = nums[j]; nums[j] = nums[j - 1]; nums[j - 1] = temp; }else{ break; } } } }
int main() { // 待排序数组 int nums[5] = {3, 1, 2, 0, 3}; // 0.计算待排序数组长度 int len = sizeof(nums) / sizeof(nums[0]); // 2.计算步长 int gap = len / 2; do{ // 1.从第一个元素开始依次取出所有用于比较元素 for (int i = gap; i < len; i++) { // 2.遍历取出前面元素进行比较 int j = i; while((j - gap) >= 0) { printf("%i > %i\n", nums[j - gap], nums[j]); // 3.如果前面一个元素大于当前元素,就交换位置 if(nums[j - gap] > nums[j]){ int temp = nums[j]; nums[j] = nums[j - gap]; nums[j - gap] = temp; }else{ break; } j--; } } // 每个小数组排序完成, 重新计算步长 gap = gap / 2; }while(gap >= 1); }
江哥提示:
对于初学者而言, 排序算法一次不易于学习太多, 咋们先来5个玩一玩, 后续继续讲解其它5个
实现步骤
在有序表中,取中间元素作为比较对象,若给定值与中间元素的要查找的数相等,则查找成功;
若给定值小于中间元素的要查找的数,则在中间元素的左半区继续查找;
若给定值大于中间元素的要查找的数,则在中间元素的右半区继续查找。
不断重复上述查找过 程,直到查找成功,或所查找的区域无数据元素,查找失败。
代码实现
int findKey(int values[], int length, int key) { // 定义一个变量记录最小索引 int min = 0; // 定义一个变量记录最大索引 int max = length - 1; // 定义一个变量记录中间索引 int mid = (min + max) * 0.5; while (min <= max) { // 如果mid对应的值 大于 key, 那么max要变小 if (values[mid] > key) { max = mid - 1; // 如果mid对应的值 小于 key, 那么min要变 }else if (values[mid] < key) { min = mid + 1; }else { return mid; } // 修改完min/max之后, 重新计算mid的值 mid = (min + max) * 0.5; } return -1; }
#include <stdio.h> void toBinary(int num) { total(num, 1, 1); } void toOct(int num) { total(num, 7, 3); } void toHex(int num) { total(num, 15, 4); } void total(int num , int base, int offset) { // 1.定义表用于查询结果 char cs[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; // 2.定义保存结果的数组 char rs[32]; // 计算最大的角标位置 int length = sizeof(rs)/sizeof(char); int pos = length;//8 while (num != 0) { int index = num & base; rs[--pos] = cs[index]; num = num >> offset; } for (int i = pos; i < length; i++) { printf("%c", rs[i]); } printf("\n"); } int main() { toBinary(9); return 0; }
二维数的初始化可分为两种:
定义的同时初始化
int a[2][3]={ {80,75,92}, {61,65,71}};
int a[2][3];
a[0][0] = 80;
a[0][1] = 75;
a[0][2] = 92;
a[1][0] = 61;
a[1][1] = 65;
a[1][2] = 71;
int a[2][3]={ {80,75,92}, {61,65,71}};
int a[2][3]={ 80,75,92,61,65,71};
int a[][3]={{1,2,3},{4,5,6}};
int a[][3]={1,2,3,4,5,6};
int a[][3]={{1},{4,5}};
int a[][3]={1,2,3,4};
- 注意: 有些人可能想不明白,为什么可以省略行数,但不可以省略列数。也有人可能会问,可不可以只指定行数,但是省略列数?其实这个问题很简单,如果我们这样写:
int a[2][] = {1, 2, 3, 4, 5, 6}; // 错误写法
大家都知道,二维数组会先存放第1行的元素,由于不确定列数,也就是不确定第1行要存放多少个元素,所以这里会产生很多种情况,可能1、2是属于第1行的,也可能1、2、3、4是第一行的,甚至1、2、3、4、5、6全部都是属于第1行的
int a[2][3]={[1][2]=10};
int a[2][3]={[1]={1,2,3}}
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("%c", cs[0][0]);// 第一个[0]取出一维数组, 第二个[0]取出一维数组中对应的元素
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
for (int i = 0; i < 2; i++) { // 外循环取出一维数组
// i
for (int j = 0; j < 3; j++) {// 内循环取出一维数组的每个元素
printf("%c", cs[i][j]);
}
printf("\n");
}
注意: 必须强调的是,a[0],a[1],a[2]不能当作下标变量使用,它们是数组名,不是一个单纯的下标变量
#include <stdio.h>
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
// cs == &cs == &cs[0] == &cs[0][0]
printf("cs = %p\n", cs); // 0060FEAA
printf("&cs = %p\n", &cs); // 0060FEAA
printf("&cs[0] = %p\n", &cs[0]); // 0060FEAA
printf("&cs[0][0] = %p\n", &cs[0][0]); // 0060FEAA
return 0;
}
#include <stdio.h> // 和一位数组一样, 只看形参是基本类型还是数组类型 // 如果是基本类型在函数中修改形参不会影响实参 void change(char ch){ ch = 'n'; } int main() { char cs[2][3] = { {'a', 'b', 'c'}, {'d', 'e', 'f'} }; printf("cs[0][0] = %c\n", cs[0][0]); // a change(cs[0][0]); printf("cs[0][0] = %c\n", cs[0][0]); // a return 0; }
#include <stdio.h> // 和一位数组一样, 只看形参是基本类型还是数组类型 // 如果是数组类型在函数中修改形参会影响实参 void change(char ch[]){ ch[0] = 'n'; } int main() { char cs[2][3] = { {'a', 'b', 'c'}, {'d', 'e', 'f'} }; printf("cs[0][0] = %c\n", cs[0][0]); // a change(cs[0]); printf("cs[0][0] = %c\n", cs[0][0]); // n return 0; }
#include <stdio.h> // 和一位数组一样, 只看形参是基本类型还是数组类型 // 如果是数组类型在函数中修改形参会影响实参 void change(char ch[][3]){ ch[0][0] = 'n'; } int main() { char cs[2][3] = { {'a', 'b', 'c'}, {'d', 'e', 'f'} }; printf("cs[0][0] = %c\n", cs[0][0]); // a change(cs); printf("cs[0][0] = %c\n", cs[0][0]); // n return 0; }
void test(char cs[2][]) // 错误写法
{
printf("我被执行了\n");
}
void test(char cs[2][3]) // 正确写法
{
printf("我被执行了\n");
}
void test(char cs[][3]) // 正确写法
{
printf("我被执行了\n");
}
void test(char cs[2][3])
{
int row = sizeof(cs); // 输出4或8
printf("row = %zu\n", row);
}
void test(char cs[2][3])
{
size_t col = sizeof(cs[0]); // 输出3
printf("col = %zd\n", col);
}
玩家通过键盘录入 w,s,a,d控制小人向不同方向移动,其中w代表向上移动,s代表向 下移动,a代表向左移动,d 代表向右移动,当小人移动到出口位置,玩家胜利
思路:
1.定义二维数组存放地图
######
#O #
# ## #
# # #
## #
######
char name[9] = "lnj"; //在内存中以“\0”结束, \0ASCII码值是0
char name1[9] = {'l','n','j','\0'};
char name2[9] = {'l','n','j',0};
// 当数组元素个数大于存储字符内容时, 未被初始化的部分默认值是0, 所以下面也可以看做是一个字符串
char name3[9] = {'l','n','j'};
//省略元素个数时, 不能省略末尾的\n
// 不正确地写法,结尾没有\0 ,只是普通的字符数组
char name4[] = {'l','n','j'};
// "中间不能包含\0", 因为\0是字符串的结束标志
// \0的作用:字符串结束的标志
char name[] = "c\0ool";
printf("name = %s\n",name);
输出结果: c
char chs[] = "lnj";
printf("%s\n", chs);
char name[] = {'c', 'o', 'o', 'l' , '\0'};
char name2[] = {'l', 'n', 'j'};
printf("name2 = %s\n", name2); // 输出结果: lnjcool
char ch[10];
scanf("%s",ch);
char ch[] = "lnj";
puts(ch); //输出结果: lnj
- puts函数完全可以由printf函数取代。当需要按一定格式输出时,通常使用printf函数
char ch[30];
gets(ch); // 输入:lnj
puts(ch); // 输出:lnj
- 可以看出当输入的字符串中含有空格时,输出仍为全部字符串。说明gets函数并不以空格作为字符串输入结束的标志,而只以回车作为输入结束。这是与scanf函数不同的。
- 注意gets很容易导致数组下标越界,是一个不安全的字符串操作函数
char name[] = "it666";
int size = sizeof(name);// 包含\0
printf("size = %d\n", size); //输出结果:6
char name[] = "it666";
size_t len = strlen(name2);
printf("len = %lu\n", len); //输出结果:5
/** * 自定义方法计算字符串的长度 * @param name 需要计算的字符串 * @return 不包含\0的长度 */ int myStrlen2(char str[]) { // 1.定义变量保存字符串的长度 int length = 0; while (str[length] != '\0') { length++;//1 2 3 4 } return length; } /** * 自定义方法计算字符串的长度 * @param name 需要计算的字符串 * @param count 字符串的总长度 * @return 不包含\0的长度 */ int myStrlen(char str[], int count) { // 1.定义变量保存字符串的长度 int length = 0; // 2.通过遍历取出字符串中的所有字符逐个比较 for (int i = 0; i < count; i++) { // 3.判断是否是字符串结尾 if (str[i] == '\0') { return length; } length++; } return length; }
char oldStr[100] = "welcome to";
char newStr[20] = " lnj";
strcat(oldStr, newStr);
puts(oldStr); //输出: welcome to lnj"
- 本程序把初始化赋值的字符数组与动态赋值的字符串连接起来。要注意的是,字符数组1应定义足 够的长度,否则不能全部装入被连接的字符串。
- 格式: strcpy(字符数组名1,字符数组名2)
- 功能:把字符数组2中的字符串拷贝到字符数组1中。串结束标志“\0”也一同拷贝。字符数名2, 也可以是一个字符串常量。这时相当于把一个字符串赋予一个字符数组。char oldStr[100] = "welcome to";
char newStr[50] = " lnj";
strcpy(oldStr, newStr);
puts(oldStr); // 输出结果: lnj // 原有数据会被覆盖
- 本函数要求字符数组1应有足够的长度,否则不能全部装入所拷贝的字符串。
char oldStr[100] = "0";
char newStr[50] = "1";
printf("%d", strcmp(oldStr, newStr)); //输出结果:-1
char oldStr[100] = "1";
char newStr[50] = "1";
printf("%d", strcmp(oldStr, newStr)); //输出结果:0
char oldStr[100] = "1";
char newStr[50] = "0";
printf("%d", strcmp(oldStr, newStr)); //输出结果:1
char names[2][10] = { {'l','n','j','\0'}, {'l','y','h','\0'} };
char names2[2][10] = { {"lnj"}, {"lyh"} };
char names3[2][10] = { "lnj", "lyh" };
什么是地址
地址与内存单元中的数据是两个完全不同的概念
变量地址:
int num = 6; // 占用4个字节
//那么变量num的地址为: 0ff06
char c = 'a'; // 占用1个字节
//那么变量c的地址为:0ff05
在计算机中所有数据都存储在内存单元中,而每个内存单元都有一个对应的地址, 只要通过这个地址就能找到对应单元中存储的数据.
由于通过地址能找到所需的变量单元,所以我们说该地址指向了该变量单元。将地址形象化的称为“指针”
内存单元的指针(地址)和内存单元的内容是两个不同的概念。
int age;// 定义一个普通变量
num = 10;
int *pnAge; // 定义一个指针变量
pnAge = &age;
char ch = 'a';
char *p; // 一个用于指向字符型变量的指针
p = &ch;
int num = 666;
int *q; // 一个用于指向整型变量的指针
q = #
- 其中,*表示这是一个指针变量
- 变量名即为定义的指针变量名
- 类型说明符表示本指针变量所指向的变量的数据类型
int a = 5;
int *p = &a;
int a = 5;
int *p;
p=&a;
int *p=NULL;
int *q=0;
int *p;
p = 250; // 错误写法
int *p;
*p=&a; //错误写法
注意点:
指针的指向是可以改变的
int a = 5;
int *p = &a;
int b = 10;
p = &b; // 修改指针指向
int *p=NULL; // 定义指针变量
int a = 5;
int *p = &a;
printf("a = %d", *p); // 访问指针变量
在同一种编译器环境下,一个指针变量所占用的内存空间是固定的。
虽然在同一种编译器下, 所有指针占用的内存空间是一样的,但不同类型的变量却占不同的字节数
char c = 'a';
char *cp;
cp = &c;
char **cp2;
cp2 = &cp;
printf("c = %c", **cp2);
int ***m1; //取值***m1
int *****m2; //取值*****m2
##数组指针的概念及定义
printf(“%p %p”, &(a[0]), a); //输出结果:0x1100, 0x1100
- 注意: 数组名a不代表整个数组,只代表数组首元素的地址。
- “p=a;”的作用是“把a数组的首元素的地址赋给指针变量p”,而不是“把数组a各元素的值赋给 p”
int main (void)
{
int a[5] = {2, 4, 6, 8, 22};
int *p;
// p = &(a[0]);
p = a;
printf(“%d %d\n”,a[0],*p); // 输出结果: 2, 2
}
- 如果指针变量p已指向数组中的一个元素,则p+1
指向
同一数组中的下一个元素,p-1指向
同 一数组中的上一个元素。
int x[10];
x++; //错误
int* p = x;
p++; //正确
char string[]=”I love lnj!”;
printf("%s\n",string);
// 数组名保存的是数组第0个元素的地址, 指针也可以保存第0个元素的地址
char *str = "abc"
har *str = "lnj";
for(int i = 0; i < strlen(str);i++)
{
printf("%c-", *(str+i)); // 输出结果:l-n-j
}
// + 使用字符数组来保存的字符串是保存栈里的,保存栈里面东西是可读可写,所有可以修改字符串中的的字符
// + 使用字符指针来保存字符串,它保存的是字符串常量地址,常量区是只读的,所以我们不可以修改字符串中的字符
char *str = "lnj";
*(str+2) = 'y'; // 错误
// 错误的原因是:str是一个野指针,他并没有指向某一块内存空间
// 所以不允许这样写如果给str分配内存空间是可以这样用 的
char *str;
scanf("%s", str);
返回值类型 (*指针变量名)(形参1, 形参2, ...);
int sum(int a,int b)
{
return a + b;
}
int (*p)(int,int);
p = sum;
指针函数定义技巧
应用场景
注意点:
struct 结构体名{
类型名1 成员名1;
类型名2 成员名2;
……
类型名n 成员名n;
};
struct Student {
char *name; // 姓名
int age; // 年龄
float height; // 身高
};
定好好结构体类型之后, 我们就可以利用我们定义的结构体类型来定义结构体变量
格式: struct 结构体名 结构体变量名;
先定义结构体类型,再定义变量
struct Student {
char *name;
int age;
};
struct Student stu;
struct Student {
char *name;
int age;
} stu;
struct {
char *name;
int age;
} stu;
- 第三种方法与第二种方法的区别在于,第三种方法中省去了结构体类型名称,而直接给出结构变量,这种结构体最大的问题是结构体类型不能复用
结构体变量名.成员名
struct Student {
char *name;
int age;
};
struct Student stu;
// 访问stu的age成员
stu.age = 27;
printf("age = %d", stu.age);
struct Student {
char *name;
int age;
};
struct Student stu = {“lnj", 27};
struct Student {
char *name;
int age;
};
struct Student stu = {.age = 35, .name = “lnj"};
struct Student {
char *name;
int age;
};
struct Student stu;
stu.name = "lnj";
stu.age = 35;
struct Student {
char *name;
int age;
};
struct Student stu;
stu2 = (struct Student){"lnj", 35};
//定义一个全局结构体,作用域到文件末尾 struct Person{ int age; char *name; }; int main(int argc, const char * argv[]) { //定义局部结构体名为Person,会屏蔽全局结构体 //局部结构体作用域,从定义开始到“}”块结束 struct Person{ int age; }; // 使用局部结构体类型 struct Person pp; pp.age = 50; pp.name = "zbz"; test(); return 0; } void test() { //使用全局的结构体定义结构体变量p struct Person p = {10,"sb"}; printf("%d,%s\n",p.age,p.name); }
struct 结构体类型名称 数组名称[元素个数]
struct Student {
char *name;
int age;
};
struct Student stu[2];
struct Student {
char *name;
int age;
};
struct Student stu[2] = {{"lnj", 35},{"zs", 18}};
struct Student {
char *name;
int age;
};
struct Student stu[2];
stu[0] = {"lnj", 35};
stu[1] = {"zs", 18};
struct 结构名 *结构指针变量名
// 定义一个结构体类型 struct Student { char *name; int age; }; // 定义一个结构体变量 struct Student stu = {“lnj", 18}; // 定义一个指向结构体的指针变量 struct Student *p; // 指向结构体变量stu p = &stu; /* 这时候可以用3种方式访问结构体的成员 */ // 方式1:结构体变量名.成员名 printf("name=%s, age = %d \n", stu.name, stu.age); // 方式2:(*指针变量名).成员名 printf("name=%s, age = %d \n", (*p).name, (*p).age); // 方式3:指针变量名->成员名 printf("name=%s, age = %d \n", p->name, p->age); return 0; }
- (pstu)两侧的括号不可少,因为成员符“.”的优先级高于“”。
- 如去掉括号写作pstu.num则等效于(pstu.num),这样,意义就完全不对了。
+多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的起始地址的值是 某个数k的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。
- 这种强制的要求一来简化了处理器与内存之间传输系统的设计,二来可以提升读取数据的速度。比如这么一种处理器,它每次读写内存的时候都从某个8倍数的地址开始,一次读出或写入8个字节的数据,假如软件能 保证double类型的数据都从8倍数地址开始,那么读或写一个double类型数据就只需要一次内存操作。否则,我们就可能需要两次内存操作才能完成这个动作,因为数据或许恰好横跨在两个符合对齐要求的8字节 内存块上
struct Person{
int age; // 4
char ch; // 1
double score; // 8
};
struct Person p;
printf("sizeof = %i\n", sizeof(p)); // 16
struct Person{
int age; // 4
double score; // 8
char ch; // 1
};
struct Person p;
printf("sizeof = %i\n", sizeof(p)); // 24
struct Date{
int month;
int day;
int year;
}
struct stu{
int num;
char *name;
char sex;
struct Date birthday;
Float score;
}
- 注意:
- 结构体不可以嵌套自己变量,可以嵌套指向自己这种类型的指针
struct Student { int age; struct Student stu; };
- 1
- 2
- 3
- 4
struct Date {
int year;
int month;
int day;
};
struct Student {
char *name;
struct Date birthday;
};
struct Student stu;
stu.birthday.year = 1986;
stu.birthday.month = 9;
stu.birthday.day = 10;
struct Person{
char *name;
int age;
};
struct Person p1 = {"lnj", 35};
struct Person p2;
p2 = p1;
p2.name = "zs"; // 修改p2不会影响p1
printf("p1.name = %s\n", p1.name); // lnj
printf("p2.name = %s\n", p2.name); // zs
#include <stdio.h> struct Person{ char *name; int age; }; void test(struct Person per); int main() { struct Person p1 = {"lnj", 35}; printf("p1.name = %s\n", p1.name); // lnj test(p1); printf("p1.name = %s\n", p1.name); // lnj return 0; } void test(struct Person per){ per.name = "zs"; }
union 共用体名{
数据类型 属性名称;
数据类型 属性名称;
... ....
};
union 共用体名 共用体变量名称;
union Test{
int age;
char ch;
};
union Test t;
printf("sizeof(p) = %i\n", sizeof(t));
t.age = 33;
printf("t.age = %i\n", t.age); // 33
t.ch = 'a';
printf("t.ch = %c\n", t.ch); // a
printf("t.age = %i\n", t.age); // 97
什么是枚举类型?
枚举类型的定义
enum 枚举名 {
枚举元素1,
枚举元素2,
……
};
// 表示一年四季
enum Season {
Spring,
Summer,
Autumn,
Winter
};
enum Season {
Spring,
Summer,
Autumn,
Winter
};
enum Season s;
enum Season {
Spring,
Summer,
Autumn,
Winter
} s;
enum {
Spring,
Summer,
Autumn,
Winter
} s;
enum Season {
Spring,
Summer,
Autumn,
Winter
} s;
s = Spring; // 等价于 s = 0;
s = 3; // 等价于 s = winter;
printf("%d", s);
enum Season {
Spring,
Summer,
Autumn,
Winter
};
// 也就是说spring的值为0,summer的值为1,autumn的值为2,winter的值为3
enum Season {
Spring = 9,
Summer,
Autumn,
Winter
};
// 也就是说spring的值为9,summer的值为10,autumn的值为11,winter的值为12
auto int num; // 等价于 int num;
register int num;
#include <stdio.h> void test(); int main() { test(); test(); test(); return 0; } void test(){ static int num = 0; // 局部变量 num++; // 如果不加static输出 1 1 1 // 如果添加static输出 1 2 3 printf("num = %i\n", num); }
// A文件中的代码
int num; // 和B文件中的num共享
void test(){
printf("ds.c中的 num = %i\n", num);
}
// B文件中的代码
#include <stdio.h>
#include "ds.h"
int num; // 和A文件中的num共享
int main()
{
num = 666;
test(); // test中输出666
return 0;
}
// A文件中的代码
static int num; // 不和B文件中的num共享
void test(){
printf("ds.c中的 num = %i\n", num);
}
// B文件中的代码
#include <stdio.h>
#include "ds.h"
int num; // 不和A文件中的num共享
int main()
{
num = 666;
test(); // test中输出0
return 0;
}
#include <stdio.h>
int main()
{
extern int num;
num = 998; // 使用时并没有存储空间可用, 所以声明了也没用
int num; // 这里才会开辟
printf("num = %i\n", num);
return 0;
}
#include <stdio.h>
int main()
{
extern int num; // 声明我们有名称叫做num变量
num = 998; // 使用时已经有对应的存储空间
printf("num = %i\n", num);
return 0;
}
int num; // 全局变量, 程序启动就会分配存储空间
内部函数:只能在本文件中访问的函数
外部函数:可以在本文件中以及其他的文件中访问的函数
默认情况下所有的函数都是外部函数
static 作用
static int sum(int num1,int num2);
static int sum(int num1,int num2)
{
return num1 + num2;
}
extern int sum(int num1,int num2);
extern int sum(int num1,int num2)
{
return num1 + num2;
}
- 注意点:
- 由于默认情况下所有的函数都是外部函数, 所以extern一般会省略
- 如果只有函数声明添加了static与extern, 而定义中没有添加static与extern, 那么无效
#include <stdio.h>
int main(){
printf("hello lnj\n");
return 0;
}
#define 标识符 字符串
#include <stdio.h> // 源程序中所有的宏名PI在编译预处理的时候都会被3.14所代替 #define PI 3.14 // 根据圆的半径计radius算周长 float girth(float radius) { return 2 * PI *radius; } int main () { float g = girth(2); printf("周长为:%f", g); return 0; }
#define R 10
int main ()
{
char *s = "Radio"; // 在第1行定义了一个叫R的宏,但是第4行中"Radio"里面的'R'并不会被替换成10
return 0;
}
#define I 100
int main ()
{
int i[3] = I;
return 0;
}
#define PI 3.14
int main ()
{
printf("%f", PI);
return 0;
}
#undef PI
void test()
{
printf("%f", PI); // 不能使用
}
#define R 3.0
#define PI 3.14
#define L 2*PI*R
#define S PI*R*R
#define String char *
int main(int argc, const char * argv[])
{
String str = "This is a string!";
return 0;
}
#define 宏名(形参表) 字符串
// 第1行中定义了一个带有2个参数的宏average,
#define average(a, b) (a+b)/2
int main ()
{
// 第4行其实会被替换成:int a = (10 + 4)/2;,
int a = average(10, 4);
// 输出结果为:7是不是感觉这个宏有点像函数呢?
printf("平均值:%d", a);
return 0;
}
#define average (a, b) (a+b)/2
int main ()
{
int a = average(10, 4);
return 0;
}
注意第1行的宏定义,宏名average跟(a, b)之间是有空格的,于是,第5行就变成了这样:
int a = (a, b) (a+b)/2(10, 4);
这个肯定是编译不通过的
#include <stdio.h>
// 下面定义一个宏D(a),作用是返回a的2倍数值:
#define D(a) 2*a
// 如果定义宏的时候不用小括号括住参数
int main ()
{
// 将被替换成int b = 2*3+4;,输出结果10,如果定义宏的时候用小括号括住参数,把上面的第3行改成:#define D(a) 2*(a),注意右边的a是有括号的,第7行将被替换成int b = 2*(3+4);,输出结果14
int b = D(3+4);
printf("%d", b);
return 0;
}
#include <stdio.h>
// 下面定义一个宏P(a),作用是返回a的平方
#define Pow(a) (a) * (a) // 如果不用小括号括住计算结果
int main(int argc, const char * argv[]) {
// 代码被替换为:int b = (10) * (10) / (2) * (2);
// 简化之后:int b = 10 * (10 / 2) * 2;,最后变量b为:100
int b = Pow(10) / Pow(2);
printf("%d", b);
return 0;
}
#include <stdio.h>
// 计算结果用括号括起来
#define Pow(a) ( (a) * (a) )
int main(int argc, const char * argv[]) {
// 代码被替换为:int b = ( (10) * (10) ) / ( (2) * (2) );
// 简化之后:int b = (10 * 10) / (2 *2);,最后输出结果:25
int b = Pow(10) / Pow(2);
printf("%d", b);
return 0;
}
#if 常量表达式
..code1...
#else
..code2...
#endif
#define SCORE 67
#if SCORE > 90
printf("优秀\n");
#else
printf("不及格\n");
#endif
#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif
#define SCORE 67
#if SCORE > 90
printf("优秀\n");
#elif SCORE > 60
printf("良好\n");
#else
printf("不及格\n");
#endif
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。