鱼C论坛

 找回密码
 立即注册
查看: 2649|回复: 3

[技术交流] C 快速学习入门教程

[复制链接]
发表于 2014-10-28 18:51:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 lies_for_L 于 2014-10-28 18:57 编辑

转自 http://learnxinyminutes.com/docs/zh-cn/c-cn/
源文件下载: learnc-cn.zip (7.74 KB, 下载次数: 17)


  1. // 单行注释以//开始。(仅适用于C99或更新的版本。)

  2. /*
  3. 多行注释是这个样子的。(C89也适用。)
  4. */

  5. // 常数: #define 关键词
  6. #define DAYS_IN_YEAR 365

  7. // 以枚举的方式定义常数
  8. enum days {SUN = 1, MON, TUE, WED, THU, FRI, SAT};
  9. // MON自动被定义为2,TUE被定义为3,以此类推。

  10. // 用#include来导入头文件
  11. #include <stdlib.h>
  12. #include <stdio.h>
  13. #include <string.h>

  14. // <尖括号>间的文件名是C标准库的头文件。
  15. // 标准库以外的头文件,使用双引号代替尖括号。
  16. #include "my_header.h"

  17. // 函数的签名可以事先在.h文件中定义,
  18. // 也可以直接在.c文件的头部定义。
  19. void function_1(char c);
  20. void function_2(void);

  21. // 如果函数出现在main()之后,那么必须在main()之前
  22. // 先声明一个函数原型
  23. int add_two_ints(int x1, int x2); // 函数原型

  24. // 你的程序的入口是一个返回值为整型的main函数
  25. int main() {

  26. // 用printf打印到标准输出,可以设定格式,
  27. // %d 代表整数, \n 代表换行
  28. printf("%d\n", 0); // => 打印 0
  29. // 所有的语句都要以分号结束

  30. ///////////////////////////////////////
  31. // 类型
  32. ///////////////////////////////////////

  33. // 在使用变量之前我们必须先声明它们。
  34. // 变量在声明时需要指明其类型,而类型能够告诉系统这个变量所占用的空间

  35. // int型(整型)变量一般占用4个字节
  36. int x_int = 0;

  37. // short型(短整型)变量一般占用2个字节
  38. short x_short = 0;

  39. // char型(字符型)变量会占用1个字节
  40. char x_char = 0;
  41. char y_char = 'y'; // 字符变量的字面值需要用单引号包住

  42. // long型(长整型)一般需要4个字节到8个字节; 而long long型则至少需要8个字节(64位)

  43. long x_long = 0;
  44. long long x_long_long = 0;

  45. // float一般是用32位表示的浮点数字
  46. float x_float = 0.0;

  47. // double一般是用64位表示的浮点数字
  48. double x_double = 0.0;

  49. // 整数类型也可以有无符号的类型表示。这样这些变量就无法表示负数
  50. // 但是无符号整数所能表示的范围就可以比原来的整数大一些

  51. unsigned short ux_short;
  52. unsigned int ux_int;
  53. unsigned long long ux_long_long;

  54. // 单引号内的字符是机器的字符集中的整数。
  55. '0' // => 在ASCII字符集中是48
  56. 'A' // => 在ASCII字符集中是65

  57. // char类型一定会占用1个字节,但是其他的类型却会因具体机器的不同而各异
  58. // sizeof(T) 可以返回T类型在运行的机器上占用多少个字节
  59. // 这样你的代码就可以在各处正确运行了
  60. // sizeof(obj)返回表达式(变量、字面量等)的尺寸
  61. printf("%zu\n", sizeof(int)); // => 4 (大多数的机器字长为4)

  62. // 如果`sizeof`的参数是一个表达式,那么这个参数不会被演算(VLA例外,见下)
  63. // 它产生的值是编译期的常数
  64. int a = 1;
  65. // size_t是一个无符号整型,表示对象的尺寸,至少2个字节
  66. size_t size = sizeof(a++); // a++ 不会被演算
  67. printf("sizeof(a++) = %zu where a = %d\n", size, a);
  68. // 打印 "sizeof(a++) = 4 where a = 1" (在32位架构上)

  69. // 数组必须要被初始化为具体的长度
  70. char my_char_array[20]; // 这个数组占据 1 * 20 = 20 个字节
  71. int my_int_array[20]; // 这个数组占据 4 * 20 = 80 个字节
  72.                       // (这里我们假设字长为4)


  73. // 可以用下面的方法把数组初始化为0:
  74. char my_array[20] = {0};

  75. // 索引数组和其他语言类似 -- 好吧,其实是其他的语言像C
  76. my_array[0]; // => 0

  77. // 数组是可变的,其实就是内存的映射!
  78. my_array[1] = 2;
  79. printf("%d\n", my_array[1]); // => 2

  80. // 在C99 (C11中是可选特性),变长数组(VLA)也可以声明长度。
  81. // 其长度不用是编译期常量。
  82. printf("Enter the array size: "); // 询问用户数组长度
  83. char buf[0x100];
  84. fgets(buf, sizeof buf, stdin);

  85. // stroul 将字符串解析为无符号整数
  86. size_t size = strtoul(buf, NULL, 10);
  87. int var_length_array[size]; // 声明VLA
  88. printf("sizeof array = %zu\n", sizeof var_length_array);

  89. // 上述程序可能的输出为:
  90. // > Enter the array size: 10
  91. // > sizeof array = 40

  92. // 字符串就是以 NUL (0x00) 这个字符结尾的字符数组,
  93. // NUL可以用'\0'来表示.
  94. // (在字符串字面量中我们不必输入这个字符,编译器会自动添加的)
  95. char a_string[20] = "This is a string";
  96. printf("%s\n", a_string); // %s 可以对字符串进行格式化
  97. /*
  98. 也许你会注意到 a_string 实际上只有16个字节长.
  99. 第17个字节是一个空字符(NUL)
  100. 而第18, 19 和 20 个字符的值是未定义。
  101. */

  102. printf("%d\n", a_string[16]); // => 0
  103. //  byte #17值为0(18,19,20同样为0)

  104. // 单引号间的字符是字符字面量
  105. // 它的类型是`int`,而 *不是* `char`
  106. // (由于历史原因)
  107. int cha = 'a'; // 合法
  108. char chb = 'a'; // 同样合法 (隐式类型转换

  109. // 多维数组
  110. int multi_array[2][5] = {
  111.         {1, 2, 3, 4, 5},
  112.         {6, 7, 8, 9, 0}
  113.     }
  114. // 获取元素
  115. int array_int = multi_array[0][2]; // => 3

  116. ///////////////////////////////////////
  117. // 操作符
  118. ///////////////////////////////////////

  119. // 多个变量声明的简写
  120. int i1 = 1, i2 = 2;
  121. float f1 = 1.0, f2 = 2.0;

  122. int a, b, c;
  123. a = b = c = 0;

  124. // 算数运算直截了当
  125. i1 + i2; // => 3
  126. i2 - i1; // => 1
  127. i2 * i1; // => 2
  128. i1 / i2; // => 0 (0.5,但会被化整为 0)

  129. f1 / f2; // => 0.5, 也许会有很小的误差
  130. // 浮点数和浮点数运算都是近似值

  131. // 取余运算
  132. 11 % 3; // => 2

  133. // 你多半会觉得比较操作符很熟悉, 不过C中没有布尔类型
  134. // 而是用整形替代
  135. // (C99中有_Bool或bool。)
  136. // 0为假, 其他均为真. (比较操作符的返回值总是返回0或1)
  137. 3 == 2; // => 0 (false)
  138. 3 != 2; // => 1 (true)
  139. 3 > 2; // => 1
  140. 3 < 2; // => 0
  141. 2 <= 2; // => 1
  142. 2 >= 2; // => 1

  143. // C不是Python —— 连续比较不合法
  144. int a = 1;
  145. // 错误
  146. int between_0_and_2 = 0 < a < 2;
  147. // 正确
  148. int between_0_and_2 = 0 < a && a < 2;

  149. // 逻辑运算符适用于整数
  150. !3; // => 0 (非)
  151. !0; // => 1
  152. 1 && 1; // => 1 (且)
  153. 0 && 1; // => 0
  154. 0 || 1; // => 1 (或)
  155. 0 || 0; // => 0

  156. // 条件表达式 ( ? : )
  157. int a = 5;
  158. int b = 10;
  159. int z;
  160. z = (a > b) ? a : b; //  10 “若a > b返回a,否则返回b。”

  161. // 增、减
  162. char *s = "iLoveC"
  163. int j = 0;
  164. s[j++]; // "i" 返回s的第j项,然后增加j的值。
  165. j = 0;
  166. s[++j]; // => "L"  增加j的值,然后返回s的第j项。
  167. // j-- 和 --j 同理

  168. // 位运算
  169. ~0x0F; // => 0xF0 (取反)
  170. 0x0F & 0xF0; // => 0x00 (和)
  171. 0x0F | 0xF0; // => 0xFF (或)
  172. 0x04 ^ 0x0F; // => 0x0B (异或)
  173. 0x01 << 1; // => 0x02 (左移1位)
  174. 0x02 >> 1; // => 0x01 (右移1位)

  175. // 对有符号整数进行移位操作要小心 —— 以下未定义:
  176. // 有符号整数位移至符号位 int a = 1 << 32
  177. // 左移位一个负数 int a = -1 << 2
  178. // 移位超过或等于该类型数值的长度
  179. // int a = 1 << 32; // 假定int32位


  180. ///////////////////////////////////////
  181. // 控制结构
  182. ///////////////////////////////////////

  183. if (0) {
  184.   printf("I am never run\n");
  185. } else if (0) {
  186.   printf("I am also never run\n");
  187. } else {
  188.   printf("I print\n");
  189. }

  190. // While循环
  191. int ii = 0;
  192. while (ii < 10) { // 任何非0的值均为真
  193.     printf("%d, ", ii++); // ii++ 在取值过后自增
  194. } // =>  打印 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "

  195. printf("\n");

  196. int kk = 0;
  197. do {
  198.     printf("%d, ", kk);
  199. } while (++kk < 10); // ++kk 先自增,再被取值
  200. // => 打印 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "

  201. printf("\n");

  202. // For 循环
  203. int jj;
  204. for (jj=0; jj < 10; jj++) {
  205.     printf("%d, ", jj);
  206. } // => 打印 "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, "

  207. printf("\n");

  208. // *****注意*****:
  209. // 循环和函数必须有主体部分,如果不需要主体部分:
  210. int i;
  211.     for (i = 0; i <= 5; i++) {
  212.     ; // 使用分号表达主体(null语句)
  213. }

  214. // 多重分支:switch()
  215. switch (some_integral_expression) {
  216. case 0: // 标签必须是整数常量表达式
  217.     do_stuff();
  218.     break; // 如果不使用break,控制结构会继续执行下面的标签
  219. case 1:
  220.     do_something_else();
  221.     break;
  222. default:
  223.     // 假设 `some_integral_expression` 不匹配任何标签
  224.     fputs("error!\n", stderr);
  225.     exit(-1);
  226.     break;
  227.     }

  228. ///////////////////////////////////////
  229. // 类型转换
  230. ///////////////////////////////////////

  231. // 在C中每个变量都有类型,你可以将变量的类型进行转换
  232. // (有一定限制)

  233. int x_hex = 0x01; // 可以用16进制字面量赋值

  234. // 在类型转换时,数字本身的值会被保留下来
  235. printf("%d\n", x_hex); // => 打印 1
  236. printf("%d\n", (short) x_hex); // => 打印 1
  237. printf("%d\n", (char) x_hex); // => 打印 1

  238. // 类型转换时可能会造成溢出,而且不会抛出警告
  239. printf("%d\n", (char) 257); // => 1 (char的最大值为255,假定char为8位长)

  240. // 使用<limits.h>提供的CHAR_MAX、SCHAR_MAX和UCHAR_MAX宏可以确定`char`、`signed_char`和`unisigned char`的最大值。


  241. // 整数型和浮点型可以互相转换
  242. printf("%f\n", (float)100); // %f 格式化单精度浮点
  243. printf("%lf\n", (double)100); // %lf 格式化双精度浮点
  244. printf("%d\n", (char)100.0);

  245. ///////////////////////////////////////
  246. // 指针
  247. ///////////////////////////////////////

  248. // 指针变量是用来储存内存地址的变量
  249. // 指针变量的声明也会告诉它所指向的数据的类型
  250. // 你可以使用得到你的变量的地址,并把它们搞乱,;-)

  251. int x = 0;
  252. printf("%p\n", &x); // 用 & 来获取变量的地址
  253. // (%p 格式化一个类型为 void *的指针)
  254. // => 打印某个内存地址

  255. // 指针类型在声明中以*开头
  256. int* px, not_a_pointer; // px是一个指向int型的指针
  257. px = &x; // 把x的地址保存到px中
  258. printf("%p\n", (void *)px); // => 输出内存中的某个地址
  259. printf("%zu, %zu\n", sizeof(px), sizeof(not_a_pointer));
  260. // => 在64位系统上打印“8, 4”。

  261. // 要得到某个指针指向的内容的值,可以在指针前加一个*来取得(取消引用)
  262. // 注意: 是的,这可能让人困惑,'*'在用来声明一个指针的同时取消引用它。
  263. printf("%d\n", *px); // => 输出 0, 即x的值

  264. // 你也可以改变指针所指向的值
  265. // 此时你需要取消引用上添加括号,因为++比*的优先级更高
  266. (*px)++; // 把px所指向的值增加1
  267. printf("%d\n", *px); // => 输出 1
  268. printf("%d\n", x); // => 输出 1

  269. // 数组是分配一系列连续空间的常用方式
  270. int x_array[20];
  271. int xx;
  272. for (xx=0; xx<20; xx++) {
  273.     x_array[xx] = 20 - xx;
  274. } // 初始化 x_array 为 20, 19, 18,... 2, 1

  275. // 声明一个整型的指针,并初始化为指向x_array
  276. int* x_ptr = x_array;
  277. // x_ptr现在指向了数组的第一个元素(即整数20).
  278. // 这是因为数组通常衰减为指向它们的第一个元素的指针。
  279. // 例如,当一个数组被传递给一个函数或者绑定到一个指针时,
  280. //它衰减为(隐式转化为)一个指针。
  281. // 例外: 当数组是`&`操作符的参数:
  282. int arr[10];
  283. int (*ptr_to_arr)[10] = &arr; // &arr的类型不是`int *`!
  284.                               // 它的类型是指向数组的指针(数组由10个int组成)
  285. // 或者当数组是字符串字面量(初始化字符数组)
  286. char arr[] = "foobarbazquirk";
  287. // 或者当它是`sizeof`或`alignof`操作符的参数时:
  288. int arr[10];
  289. int *ptr = arr; // 等价于 int *ptr = &arr[0];
  290. printf("%zu, %zu\n", sizeof arr, sizeof ptr); // 应该会输出"40, 4"或"40, 8"

  291. // 指针的增减多少是依据它本身的类型而定的
  292. // (这被称为指针算术)
  293. printf("%d\n", *(x_ptr + 1)); // => 打印 19
  294. printf("%d\n", x_array[1]); // => 打印 19

  295. // 你也可以通过标准库函数malloc来实现动态分配
  296. // 这个函数接受一个代表容量的参数,参数类型为`size_t`
  297. // 系统一般会从堆区分配指定容量字节大小的空间
  298. // (在一些系统,例如嵌入式系统中这点不一定成立
  299. // C标准对此未置一词。)
  300. int *my_ptr = malloc(sizeof(*my_ptr) * 20);
  301. for (xx=0; xx<20; xx++) {
  302.     *(my_ptr + xx) = 20 - xx; // my_ptr[xx] = 20-xx
  303. } // 初始化内存为 20, 19, 18, 17... 2, 1 (类型为int)

  304. // 对未分配的内存进行取消引用会产生未定义的结果
  305. printf("%d\n", *(my_ptr + 21)); // => 谁知道会输出什么

  306. // malloc分配的区域需要手动释放
  307. // 否则没人能够再次使用这块内存,直到程序结束为止
  308. free(my_ptr);

  309. // 字符串通常是字符数组,但是经常用字符指针表示
  310. // (它是指向数组的第一个元素的指针)
  311. // 一个优良的实践是使用`const char *`来引用一个字符串字面量,
  312. // 因为字符串字面量不应当被修改(即"foo"[0] = 'a'犯了大忌)
  313. const char* my_str = "This is my very own string";
  314. printf("%c\n", *my_str); // => 'T'

  315. // 如果字符串是数组,(多半是用字符串字面量初始化的)
  316. // 情况就不一样了,字符串位于可写的内存中
  317. char foo[] = "foo";
  318. foo[0] = 'a'; // 这是合法的,foo现在包含"aoo"

  319. function_1();
  320. } // main函数结束

  321. ///////////////////////////////////////
  322. // 函数
  323. ///////////////////////////////////////

  324. // 函数声明语法:
  325. // <返回值类型> <函数名称>(<参数>)

  326. int add_two_ints(int x1, int x2){
  327.     return x1 + x2; // 用return来返回一个值
  328. }

  329. /*
  330. 函数是按值传递的。当调用一个函数的时候,传递给函数的参数
  331. 是原有值的拷贝(数组除外)。你在函数内对参数所进行的操作
  332. 不会改变该参数原有的值。

  333. 但是你可以通过指针来传递引用,这样函数就可以更改值

  334. 例子:字符串本身翻转
  335. */

  336. // 类型为void的函数没有返回值
  337. void str_reverse(char *str_in){
  338.     char tmp;
  339.     int ii = 0;
  340.     size_t len = strlen(str_in); // `strlen()`` 是C标准库函数
  341.     for(ii = 0; ii < len / 2; ii++){
  342.         tmp = str_in[ii];
  343.         str_in[ii] = str_in[len - ii - 1]; // 从倒数第ii个开始
  344.         str_in[len - ii - 1] = tmp;
  345.     }
  346. }

  347. /*
  348. char c[] = "This is a test.";
  349. str_reverse(c);
  350. printf("%s\n", c); // => ".tset a si sihT"
  351. */

  352. // 如果引用函数之外的变量,必须使用extern关键字
  353. int i = 0;
  354. void testFunc() {
  355.     extern int i; // 使用外部变量 i
  356. }

  357. // 使用static确保external变量为源文件私有
  358. static int i = 0; // 其他使用 testFunc()的文件无法访问变量i
  359. void testFunc() {
  360.     extern int i;
  361. }
  362. //**你同样可以声明函数为static**


  363. ///////////////////////////////////////
  364. // 用户自定义类型和结构
  365. ///////////////////////////////////////

  366. // Typedefs可以创建类型别名
  367. typedef int my_type;
  368. my_type my_type_var = 0;

  369. // struct是数据的集合,成员依序分配,按照
  370. // 编写的顺序
  371. struct rectangle {
  372.     int width;
  373.     int height;
  374. };

  375. // 一般而言,以下断言不成立:
  376. // sizeof(struct rectangle) == sizeof(int) + sizeof(int)
  377. //这是因为structure成员之间可能存在潜在的间隙(为了对齐)[1]

  378. void function_1(){

  379.     struct rectangle my_rec;

  380.     // 通过 . 来访问结构中的数据
  381.     my_rec.width = 10;
  382.     my_rec.height = 20;

  383.     // 你也可以声明指向结构体的指针
  384.     struct rectangle *my_rec_ptr = &my_rec;

  385.     // 通过取消引用来改变结构体的成员...
  386.     (*my_rec_ptr).width = 30;

  387.     // ... 或者用 -> 操作符作为简写提高可读性
  388.     my_rec_ptr->height = 10; // Same as (*my_rec_ptr).height = 10;
  389. }

  390. // 你也可以用typedef来给一个结构体起一个别名
  391. typedef struct rectangle rect;

  392. int area(rect r){
  393.     return r.width * r.height;
  394. }

  395. // 如果struct较大,你可以通过指针传递,避免
  396. // 复制整个struct。
  397. int area(const rect *r)
  398. {
  399.     return r->width * r->height;
  400. }

  401. ///////////////////////////////////////
  402. // 函数指针
  403. ///////////////////////////////////////
  404. /*
  405. 在运行时,函数本身也被存放到某块内存区域当中
  406. 函数指针就像其他指针一样(不过是存储一个内存地址) 但却可以被用来直接调用函数,
  407. 并且可以四处传递回调函数
  408. 但是,定义的语法初看令人有些迷惑

  409. 例子:通过指针调用str_reverse
  410. */
  411. void str_reverse_through_pointer(char *str_in) {
  412.     // 定义一个函数指针 f.
  413.     void (*f)(char *); // 签名一定要与目标函数相同
  414.     f = &str_reverse; // 将函数的地址在运行时赋给指针
  415.     (*f)(str_in); // 通过指针调用函数
  416.     // f(str_in); // 等价于这种调用方式
  417. }

  418. /*
  419. 只要函数签名是正确的,任何时候都能将任何函数赋给某个函数指针
  420. 为了可读性和简洁性,函数指针经常和typedef搭配使用:
  421. */

  422. typedef void (*my_fnp_type)(char *);

  423. // 实际声明函数指针会这么用:
  424. // ...
  425. // my_fnp_type f;

  426. // 特殊字符
  427. '\a' // bell
  428. '\n' // 换行
  429. '\t' // tab
  430. '\v' // vertical tab
  431. '\f' // formfeed
  432. '\r' // 回车
  433. '\b' // 退格
  434. '\0' // null,通常置于字符串的最后。
  435.      //   hello\n\0. 按照惯例,\0用于标记字符串的末尾。
  436. '\\' // 反斜杠
  437. '\?' // 问号
  438. '\'' // 单引号
  439. '"' // 双引号
  440. '\xhh' // 十六进制数字. 例子: '\xb' = vertical tab
  441. '\ooo' // 八进制数字. 例子: '\013' = vertical tab

  442. // 打印格式:
  443. "%d"    // 整数
  444. "%3d"   // 3位以上整数 (右对齐文本)
  445. "%s"    // 字符串
  446. "%f"    // float
  447. "%ld"   // long
  448. "%3.2f" // 左3位以上、右2位以上十进制浮
  449. "%7.4s" // (字符串同样适用)
  450. "%c"    // 字母
  451. "%p"    // 指针
  452. "%x"    // 十六进制
  453. "%o"    // 八进制
  454. "%%"    // 打印 %

  455. ///////////////////////////////////////
  456. // 演算优先级
  457. ///////////////////////////////////////
  458. //---------------------------------------------------//
  459. //        操作符                     | 组合          //
  460. //---------------------------------------------------//
  461. // () [] -> .                        | 从左到右      //
  462. // ! ~ ++ -- + = *(type)sizeof       | 从右到左      //
  463. // * / %                             | 从左到右      //
  464. // + -                               | 从左到右      //
  465. // << >>                             | 从左到右      //
  466. // < <= > >=                         | 从左到右      //
  467. // == !=                             | 从左到右      //
  468. // &                                 | 从左到右      //
  469. // ^                                 | 从左到右      //
  470. // |                                 | 从左到右      //
  471. // &&                                | 从左到右      //
  472. // ||                                | 从左到右      //
  473. // ?:                                | 从右到左      //
  474. // = += -= *= /= %= &= ^= |= <<= >>= | 从右到左      //
  475. // ,                                 | 从左到右      //
  476. //---------------------------------------------------//
复制代码






小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2014-10-29 09:40:17 | 显示全部楼层
支持个。。。。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

头像被屏蔽
发表于 2014-10-30 17:41:08 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2014-12-9 08:19:50 | 显示全部楼层
{:1_1:}谢分享
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-18 17:38

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表