计算机语言有千百种,不管什么语言,目的是要让计算机做你让他做的事情。
并且语言由以下部分组成:
C语言及函数库是自己在日常学习工作中的总结。
1. C语言
描述 | 备注 |
类型、表达式、语句 | |
字符串、字符、字节 |
|
数组、指针 | |
内存管理 | |
文件 | |
结构和联合 | |
位操作 | |
预处理器 | |
编码风格 | |
GNU 编译系统 | |
GNU make | |
GDB | |
Git | |
2. Python 语言
描述 | 备注 |
Python 小乌龟图形 | |
Python 基础 | |
Python面向对象 |
3. SQL 语句
描述 | 备注 |
SQL语言 |
2. 经典C/C++语言书籍
* C程序设计语言
* C和指针
* C专家编程
* C语言程序设计现代方法
* C Primer Plus
* C标准库
* GNU Make
* http://blog.csdn.net/suprman/article/details/1467470
* 写C源代码遵循原则: http://www.ruanyifeng.com/blog/2009/06/unix_philosophy.html
* Quora - https://www.quora.com/What-is-the-best-way-to-learn-C++-on-my-own
3. 基本概念
C语言在1972年来自贝尔实验室的Dennis Ritchie和Ken Thompson, 它们当时正在设计UNIX操作系统,
C由此而诞生。
编程语言的特性
(1) 变量与常量 - 程序处理的两种基本数据对象。
(2) 声明语句 - 说明变量的名字及类型,也可以指定变量的初值
(2) 内置数据类型 - 决定该对象可取值的集合以及可对该对象执行的操作
(3) 表达式和语句 - 指定将要进行的操作
(4) 控制结构
(5) 函数
使用C语言的七个步骤:
Step 1. 定义程序目标
Step 2. 设计程序
Step 3. 编写代码
Step 4. 编译
Step 5. 运行程序
Step 6. 测试与调式程序
Step 7. 维护与修改程序
Step 8. 总结
1. C源程序编译与链接过程
第一步: hello.c 文件 进行 预处理 变成 hello.i 文件
第二步: hello.i 文件 进行 编译 变成 hello.s 汇编文件
第三步: hello.s 文件 进行 汇编编译 变成 hello.o 目标文件
第四步: hello.o 文件 进行 链接 变成 hello.exe 文件
2. 编译和链接
1)编译并链接一个完全包含于一个源文件的C程序
cc program.c
2)编译并链接几个C源文件
cc main.c sort.c lookup.c
3) 编译一个C源文件,并把它和现存的目标文件链接在一起
cc main.o lookup.o sort.c
4) 编译单个C源文件,并产生一个目标文件
cc -c program.c
5) 链接几个目标文件
cc main.o sort.o lookup.o
-lname 表示链接器会同时在name的函数库中进行查找
3. 可执行文件包含两部分内容
一、程序和数据
二、相关的描述信息
PE格式
Win: .exe , .dll, .sys
Linux: .elf
使用PEID/PETool查看PE结构
问题:
1. 从源文件到最终生成可执行文件,要经历哪些阶段的处理?
2. 进程与程序的区别是什么? 进程与线程的区别是什么?
3. 理解PE文件格式,通过工具找到pe中的e_lfanew, entrypoint, 导入表地址。
4. 什么是PE中的EOP, OEP, 什么是ImageBase, VA, RVA?
5. PE中的Section有哪些?怎么组织和管理的? 分别起什么作用?
6. 试求下面VA对应的文件地址:
名称 name Voffset Vsize Roffset rsize 标志
.text 0000540 00069f9b 00000540 00069fc0 68000020
虚拟地址 VA=00049586 ImageBase = 00040000
求虚拟地址 VA对应的文件地址?
经典问题: main 函数执行以前,还会执行什么代码?
全局对象的构造函数会在main 函数之前执行。
经典问题:main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?
可以
int fn1(void), fn2(void), fn3(void), fn4 (void);
void main( void )
{
String str("david");
_onexit( fn1 );
_onexit( fn2 );
_onexit( fn3 );
_onexit( fn4 );
printf( "This is executed first.
" );
}
int fn1()
{
printf( "next.
" );
return 0;
}
int fn2()
{
printf( "executed " );
return 0;
}
int fn3()
{
printf( "is " );
return 0;
}
int fn4()
{
printf( "This " );
return 0;
}
The _onexit function is passed the address of a function (func) to be called when the program terminates normally.
Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters.
4. C语言内存分配
当一个C程序载入内存后,占用内存的起始到结束地址范围时,会将内存分成3个部分:
text segment(code segment): 编译后程序占用的内存空间
Data Segment: 分成两个部分,
* 初始化数据(Data Segment)
* 未初始化数据(BSS)
heap:当程序在执行期间分配内存时,内存存放在堆区 (调用malloc)
stack: 栈用来存放本地变量以及传递的参数 (RLIMIT_STACK)
问题:描述内存分配方式以及它们的区别?
1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
写一个函数找出一个整数数组中,第二大的数
/*
* 思路: 将数组第一个数假定为最大值
* 遍历后面的数,与第一个数进行比较
* 如果比最大值大,将当前最大值变成第二大值,将其变成最大值
* 如果比第二大值大,将其变为第二大值
*/
const int MINNUMBER = -32767 ; //定义最小值
// data 为定义的数组, count 为数组长度
int find_sec_max( int data[] , int count)
{
int maxnumber = data[0] ; // 假定第一个值为最大
int sec_max = MINNUMBER ; // 初始化第二小值
for ( int i = 1 ; i < count ; i++)
{
if ( data[i] > maxnumber ) // 首先跟最大值比较
{
sec_max = maxnumber ; // 把当前值给第二大值
maxnumber = data[i] ; // 把最大值给maxnumber
}
else
{
if ( data[i] > sec_max ) // 然后跟第二大值比较
sec_max = data[i] ; // 如果大于当前第二大值,赋值
}
}
return sec_max ;
}
下面的代码输出是什么,为什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) puts(">6") : puts("<=6");
}
>6
原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。
因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
评价下面代码
unsigned int zero = 0;
unsigned int compzero = 0xFFFF; //1's complement of zero
对于一个int型不是16位的处理器为说,上面的代码是不正确的。
unsigned int compzero = ~0;
下面的代码输出是什么,为什么?
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts( "Got a null pointer ");
else
puts( "Got a valid pointer ");
当使用malloc后,只有在没有足够内存的情况下会返回NULL,或是出现异常报告。
NULL一般预定义为(void *)0, 指向0地址。 malloc是在程序堆栈上分配空间,不会是0地址
malloc(0)是指分配内存大小为零
NULL是不指向任何实体
malloc(0)也是一种存在不是NULL
#include <stdio.h>
#include <stdlib.h>
int Josephu(int n, int m)
{
int flag, i, j = 0;
int *arr = (int *)malloc(n * sizeof(int));
for (i = 0; i < n; ++i)
arr[i] = 1;
for (i = 1; i < n; ++i)
{
flag = 0;
while (flag < m)
{
if (j == n)
j = 0;
if (arr[j])
++flag;
++j;
}
arr[j - 1] = 0;
printf("out %4d people is:%4d
", i, j);
}
free(arr);
return j;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
printf("last winner is %d!
", Josephu(n, m));
return 0;
}
链表实现
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int index;
struct Node *next;
}JosephuNode;
int Josephu(int n, int m)
{
int i, j;
JosephuNode *head, *tail;
head = tail = (JosephuNode *)malloc(sizeof(JosephuNode));
for (i = 1; i < n; ++i)
{
tail->index = i;
tail->next = (JosephuNode *)malloc(sizeof(JosephuNode));
tail = tail->next;
}
tail->index = i;
tail->next = head;
for (i = 1; tail != head; ++i)
{
for (j = 1; j < m; ++j)
{
tail = head;
head = head->next;
}
tail->next = head->next;
printf("第%4d个出局的人是:%4d号/n", i, head->index);
free(head);
head = tail->next;
}
i = head->index;
free(head);
return i;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
printf("最后胜利的是%d号!/n", Josephu(n, m));
return 0;
}