快速学习C语言一: Hello World
估计不会写C语言的同学也都听过C语言,从头开始快速学一下吧,以后肯定能用的上。 如果使用过其它类C的语言,如JAVA,C#等,学C的语法应该挺快的。
先快速学习并练习一些基本的语言要素,基本类型,表达式,函数,循环结构, 基本字符串操作, 基本指针操作,动态分配内存,使用结构表示复杂数据, 使用函数指针实现灵活逻辑。
虽然C是一个规模很小的语言,但也得自己多设计一些练习练手才能学会。
基本类型
我就记得char, int, 别的都不常用吧应该,用的时候再搜索。
表达式
和JAVA, C#差不多吧,不用学基本,各种算数运算符,关系运算符,逻辑运算符,逗号, 括号等的意思应该也差不多,表达式最终的结果也有类型和值。
函数
函数是最基本的抽象,基本没有什么语言没有函数的概念,它封装一系列操作, 最简单的Hello world,如下。
static void hello_world(){
printf("hello, world\n");
}
我们的练习都是随手写的函数,不需要被外部调用,所以前面加个static,表示只在 本文件内可见。
printf
输出一行的话,最后要加\n
, 常见个格式化参数有%d,%c,%s,%p等,分别表示 输出int, char, 字符串, 指针。
分支,循环结构
和别的语言差不多,不过i的声明要放在函数开头,c89就是这样。
static void n_hello_world(int n){
int i = ;
for (i = ; i < n; i++) {
printf("hello, world\n");
}
}
字符串练习,获取一个字符串的长度
库函数strlen
就是干这个的,不过我们自己可以写一个练手,c没有字符串类型, 用'\0'结尾的字符数组表示字符串,所以for循环从头滚到'\0'位置就好了。
// 字符串练习, 计算字符串长度
static int w_strlen(const char* str){
int i;
// 向后滚动指针,同时递增i,直到找到字符串结尾
for (i = ; *str != '\0'; str++, i++) {
;
}
return i;
}
const 修饰符表示这个参数不能在函数里进行更改,防止意外改动。char *就是传说中 字符串了。 写C程序得用好for语句,有各种惯用法,用好了可以写出很紧凑的程序,比如上面for语句 的第2个分号后的逗号表达式可以递增两个变量。
理解字符串的存储
第一种方式是在编译时分配的内存,是字符串常量,指针s1指向的内存不能更改。 第二种方式应该是在栈上分配的内存(不确定),可以通过指针修改其中的字符。
static void change_str_test(){
// 常量不能修改
// char* s1 = "hello"; // will core dump
char s1[] = "hello";
*s1 = 'p';
printf("%s\n", s1);
}
指针练习
指针可以进行加减的操作,每加一次就滚动过它指向的类型长度, 比如char指针就是 滚动1个字节。
// 指针练习, 反转字符串
static char* reverse(char* str){
char* ret = str;
// 滚到字符数组末尾的\0之前
char* p = str + w_strlen(str) - ;
char c; // 两个指针,一个从前往后滚,一个从后往前滚,直到就要交错之前
// 滚动的过程中交换两个指针指向的字符
for ( ; p > str; --p, ++str) {
printf("debug[reverse]: %p %p %c %c\n", p, str, *p, *str);
c = *p;
*p = *str;
*str = c;
} return ret;
}
c = *p
表示取出指针p指向的字符,赋值给变量c,*表示取值。
*p = *str
相当于p[i] = str[i]
,右边的取出来的是值,左边的取出来的也是值, 值赋值给值,看起来有些诡异,但就是这样写的。反正p = *str
肯定不对,因为p是 指针类型,*str是计算结果是字符类型。
动态分配内存
我记得TCPL前几章都没讲malloc,free等内存分配的函数,好多任务只需要在编译阶段 分配内存就够了,但比较大型复杂的程序应该都需要动态管理一些内存的。
C语言没有GC,要手工释放动态分配的内存,否则就会造成内存泄漏,所以一定要配平 资源,有malloc的地方,一定要想好它应该在哪里free。
目前我了解到的原则就有两种:
- 谁分配,谁释放
- 谁使用,谁释放
对了, malloc出来的内存要强转成你需要的指针类型,然后free时指针要滚到你动态 分配内存的起始点。
// 内存申请相关,连接两个字符串
static void concat_test(){
char* a = "hello";
char* b = "world";
//结果字符串长度为两个字符窜长度加\0的位置
int len = w_strlen(a) + w_strlen(b) + ;
// 动态分配内存
char* p = (char *)malloc(sizeof(char) * len);
char* result; // 必须判断是否分配到内存
if (p != NULL){
// 保存动态分配内存的开始指针,free时必须从这里free
result = p; //滚动p和a,直到a的末尾
while (*a != '\0') {
printf("debug[concat_test]:while a %p %c\n", a, *a);
*p++ = *a++;
} //滚动p和b,直到b的末尾
while (*b != '\0') {
printf("debug[concat_test]:while b %p %c\n", a, *a);
*p++ = *b++;
} // 末尾整个0
*p= '\0';
printf("concat_test: %s\n", result); //释放动态分配的内存
free(result);
}else{
printf("malloc error");
}
}
结构练习
C没有类,要表达复杂的数据,就得用结构了, 结构也可以用指针来指,如果是结构变量 的话,引用成员用.
,如果是指向结构的指针,引用成员用->
别的好像没啥特别的,注意动态分配结构数组后,指针滚动的边界,别使用了界外的 内存。如果结构的成员指向的内存是动态分配的花,也记得free。
没有结构,估计写不出大程序,结构应该会用的很多。
//结构练习,人员统计系统
struct customer {
char* name;
int age;
}; static void customer_manager() {
// 直接在栈上分配结构体
struct customer wawa;
struct customer* p_wawa;
struct customer* p_customers;
int n = ; char name[] = "wawa";
// char* name = "wawa"; //splint warning
char name2[] = "tiancai"; // 直接用结构名访问成员
wawa.name = name;
wawa.age = ;
printf("%s is %d years old\n", wawa.name, wawa.age); // 用指针访问结构成员
p_wawa = &wawa;
p_wawa->age = ;
printf("%s is %d years old\n", wawa.name, wawa.age); // 为员工数组动态分配内存
p_customers = (struct customer*)malloc(sizeof(struct customer) * n);
if (p_customers != NULL) {
// 设置数组第一项
p_customers->name = name;
p_customers->age = ; // 设置数组第二项
p_customers++;
p_customers->name = name2;
p_customers->age = ; // 滚动数组外面,然后反向循环到数组开始
p_customers++;
while(n-- > ){
p_customers--;
printf("%s is %d years old\n", p_customers->name, p_customers->age);
} // 释放动态分配的内存,这时候p_customers已经位于起始位置了
// 结构体里的name1, name2是在栈上分配的,不用释放
free(p_customers);
}
}
函数指针练习
好多语言都有高阶函数的特性,比如函数的参数或返回值还可以是个函数, C里也有函数指针可以达到类似的效果,用来做回调函数等。
但C的函数指针写起来比较诡异,不好记忆,不行就用typedef来重新命个名,写起来 简单一些。
下面用一个比较经典的冒泡排序来演示函数指针的使用,传递不同的比较函数可以 改变排序函数的行为,这是写复杂灵活逻辑的一种很方便的方式。
// 函数指针练习, 排序 // 正序排序的比较函数
static int cmp_default(int a, int b){
return a - b;
} // 反序排序的比较函数
static int cmp_reverse(int a, int b){
return b - a;
} // int类型的冒泡排序算法,可传入一个比较函数指针
// 类似回调函数,该函数需要两个int参数且返回int
static void sort(int* arr, int n, int (*cmp)(int, int)){
int i, j, t;
int *p, *q; p = arr; for (i = ; i < n; i++, p++) {
q = p;
for (j = i; j < n; j++, q++) {
// 调用函数指针指向的函数和使用函数一样,貌似是简单写法
if (cmp(*p, *q) > ) {
t = *p;
*p = *q;
*q = t;
}
}
}
} // 测试排序函数
static void sort_test(){
int arr[] = {, , , , };
int i, n = ; // 正向排序, 传入cmp_default函数的地址,貌似不需要&取地址
sort(arr, , cmp_default);
for (i = ; i < n; i ++) {
printf("%d%s", arr[i], i == n - ? "" : ", ");
}
printf("\n"); //反向排序,同上
sort(arr, , cmp_reverse);
for (i = ; i < n; i ++) {
printf("%d%s", arr[i], i == n - ? "" : ", ");
}
printf("\n");
}
总结
这几年断断续续看了四五遍K&R的《TCPL》了,可一直都没写过C程序,现在开始多练习 练习吧。
快速学习C语言一: Hello World的更多相关文章
- 快速学习C语言三: 开发环境, VIM配置, TCP基础,Linux开发基础,Socket开发基础
上次学了一些C开发相关的工具,这次再配置一下VIM,让开发过程更爽一些. 另外再学一些linux下网络开发的基础,好多人学C也是为了做网络开发. 开发环境 首先得有个Linux环境,有时候家里机器是W ...
- 快速学习C语言二: 编译自动化, 静态分析, 单元测试,coredump调试,性能剖析
上次的Hello world算是入门了,现在学习一些相关工具的使用 编译自动化 写好程序,首先要编译,就用gcc就好了,基本用法如下 gcc helloworld.c -o helloworld.o ...
- 快速学习C语言途径,让你少走弯路
1.标准C语言能干什么? 坦白讲,在今天软件已经发展了半个多世纪,单纯的C语言什么都干不了.标准C语言库只提供了一些通用的逻辑运算方法以及字符串处理,当然字符串在C语言看来也是一种操作内存的方法,所以 ...
- 快速学习C语言四: 造轮子,ArrayList
高级语言里的列表是最常用的数据结构,在C里造个轮子玩玩,C没有泛型,先用int练习. Collection的ADT一般有hasnext,next,add, remove操作,List一般还加了remo ...
- 学习swift语言的快速入门教程推荐
随着苹果产品越来越火爆,苹果新推出的swift必定将在很大程度上代替oc语言.学好swift语言,对于IOS工程师来讲,已经是一门必备技能. 有一些比较好的英文版教程,值得学习. 1. Swift T ...
- Dart语言快速学习上手(新手上路)
Dart语言快速学习上手(新手上路) // 声明返回值 int add(int a, int b) { return a + b; } // 不声明返回值 add2(int a, int b) { r ...
- 如何快速高效率地学习Go语言
要想快速高效率地掌握Go语言,关键是要通过不断写代码去训练,熟能生巧.方法是没问题的,但具体的路径呢?就像开车,能不能给个导航?我希望这篇文章能起到一个导航的作用,这里提供的路径,应该对很多人都适合. ...
- 大神教零基础入门如何快速高效的学习c语言开发
零基础如果更快更好的入门C语言,如何在枯燥的学习中找到属于自己的兴趣,如果把学习当成一种事务性的那以后的学习将会很难有更深入的进步,如果带着乐趣来完成学习那将越学越有意思这样才会让你有想要更深入学习的 ...
- 60分钟Python快速学习(给发哥一个交代)
60分钟Python快速学习 之前和同事谈到Python,每次下班后跑步都是在听他说,例如Python属于“胶水语言啦”,属于“解释型语言啦!”,是“面向对象的语言啦!”,另外没有数据类型,逻辑全靠空 ...
随机推荐
- double四舍五入,double四舍五入并转成string
import java.math.BigDecimal; /** * 处理一些数据类型的方法的java类 * @author ljb * */public class NumberTools { /* ...
- OOP之C#设计及其UML(反向工程)
现在总结一下C#类关键字(virtual.abstract.override.new.sealed)的使用(以C#代码体现),并再次熟悉一下OOP思想,使用UML工具EA(Enterprise Arc ...
- 【如何快速的开发一个完整的iOS直播app】(美颜篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重 ...
- sql报句柄无效。 (异常来自 HRESULT:0x80070006 (E_HANDLE))
是由于数据库连接资源被耗尽或者用完没被释放导致的. 我在字符串中加了启用连接池好了. 如果错误信息为:sql 无效操作.连接被关闭 也是这个问题导致的.
- [转载]centos7 快速安装 mariadb(mysql)
http://blog.csdn.net/default7/article/details/41973887 从最新版本的linux系统开始,默认的是 Mariadb而不是mysql! yum ins ...
- zend studio常用快捷键
1.提示符助手快捷键 alt+/ 你可以自定义 window->keys->Content assist->Binding 2.复制当前行 alt+ctrl+下 3.删除 ctrl+ ...
- hdoj 1272 小希的迷宫
上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走.但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了 ...
- 如何选择 H5 游戏引擎
原生手游市场已是红海,腾讯.网易等寡头独霸天下,H5游戏市场或将成为下一个风口.据笔者所知,很多H5游戏开发团队由于选择引擎不慎导致项目甚至团队夭折.如何选择适合团队和项目的引擎,笔者通过学习和项目实 ...
- CM+CDH安装遇到的问题
1.实在是在安装CDH的时候无法安装成功的话,只有重新启动了,下面给大家分享一个神器,按照这个脚本应该差不多就能卸载干净,然后重新安装,写一个脚本,内容如下,救命的神器呀: #!/bin/bash s ...
- Mysql主从架构的复制原理及配置详解
一.简述Mysql复制 Mysql复制是通过将mysql的某一台主机的数据复制到其他主机(slaves)上,并且在slaves上重新执行一遍来实现.主服务器每次数据操作都会将更新记录到二进制日志文件, ...