一篇Makefile文章惹起的内存指针的理解!!

转载来至:https://blog.csdn.net/sinat_36184075/article/details/54917518

《Makefile编写、链接脚本编写、裸板shell框架》


<tips>
' 工具:UtraEdit 代码编辑工具
// 此工具里面 Ctrl + h 查看ASCII码

<tips>
vi中命令模式下:
':e main.c   // 打开main.c
':bn            // 回到刚才的.c文件

1. 关于 \r \n的问题
windows系统中:
\r    回到行首    0x0d
\n    到下一行    0x0a
linux系统中,只需要一个字符:\n
hexdump -C 1.txt    // 1.txt 里面敲回车

一、关于Makefile - // 查着用即可不用学
1) env\GNU make v3.80完整版中文指南.pdf
2) 跟我一起写Makefile(后补--->)
makefile主要分为三部分:
目标文件[target]:依赖文件[dependencies]
    [1个Tab]规则


'make与makefile 的作用
make工具用来管理大型软件的编译:
- 自动判断一个程序的哪些文件需要编译,并且能用命令来执行编译操作;
- 实现类似集成开发环境(IDE)自动化编译;
makefile定义了一系列的规则来指定:
- 哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译更复杂的功能操作。
- makefile中可以执行操作系统的命令


现阶段达标:
可以写最简单的Makefile。
$:'vi Makefile
' $< 代表依赖文件
' $@ 代表目标文件
  1. /** 代码演示 - Makefile **/
  2. NAME=shell
  3. ELF=$(NAME).elf #shell.elf
  4. BIN=$(NAME).bin #shell.bin
  5. OBJS=main.o uart.o
  6. CC=arm-cortex_a9-linux-gnueabi-gcc
  7. LD=arm-cortex_a9-linux-gnueabi-ld
  8. OBJCOPY=arm-cortex_a9-linux-gnueabi-objcopy
  9. CFLAGS=-nostdlib -Wall
  10. LDFLAGS=-nostdlib -nostartfiles
  11. $(BIN):$(ELF)
  12. $(OBJCOPY) -O binary $(ELF) $(BIN)
  13. cp $(BIN) /tftpboot/
  14. $(ELF):$(OBJS)
  15. $(LD) $(LDFLAGS) -Ttext= 0x48000000 -emain $(OBJS) -o $(ELF)
  16. %.o:%.c
  17. $(CC) $(CFLAGS) -c $< -o $@
  18. clean:
  19. rm -rf $(BIN) $(ELF) $(OBJS) /tftpboot/$(BIN)
  20. // rm -vf ... 可以显示删除的文件列表提示信息

二、关于链接脚本
【链接器】
- 将若干输入文件(.o file) 根据一定规则合并为一个输出文件;
- 将标号的地址??; // 半句话
【链接脚本】
- 连接工具的输入文件;
- 链接脚本有自己的语法;
'链接脚本的作用
1) 主要用于规定如何把输入文件内的SECTION放入输出文件内;
2) 控制输出文件内各部分在程序地址空间内的布局;


链接脚本文件
$:'vi shell.lds
  1. /** 代码演示 - shell.lds **/
  2. ENTRY (main) // -emain
  3. SECTIONS {
  4. . = 0x48000000; // 指定 .text的起始位置
  5. .text : { // 代码段
  6. main.o (.text) // 可执行文件以main.o最先链接
  7. * (.text)
  8. }
  9. .data : { // 数据段
  10. * (.data)
  11. }
  12. .bss : { // BSS堆
  13. * (.bss)
  14. }
  15. }
  16. // 写完链接脚本文件后,Makefile 文件中对应可修改此句:
  17. $(ELF):$(OBJS)
  18. $(LD) $(LDFLAGS) -Tshell.lds $(OBJS) -o $(ELF)

' C语言指针复习【关注内存】
  1. char s1;
  2. char* s2;
  3. char s3[ 10];
  4. char* s4[ 10];
  5. s1 = 'a';
  6. s2 = 'b'; // 不合理,警告
  7. s2 = "12345";
  8. s2++;
  9. (*s2)++; // 【段错误】,12345是常量,在只读代码段,自增段错误!
  10. strcpy (s2, "hello"); // 【段错误】,(readonly)非法写入。
  11. s3[ 0] = 'c';
  12. s3[ 0] += 1;
  13. s3++; // 【错误】数组首地址,是地址常量,自增编译不通过(非左值)
  14. strcpy (s3, "world");

  1. /** 特例演示 - 指针 **/
  2. #include <stdio.h>
  3. #include <string.h>
  4. int main (void) {
  5. char* s1 = "hello";
  6. char* s2 = s1; // 【内存】s2 在代码段 - 只读常量区
  7. strcpy (s2, "world..");
  8. printf ( "s2 = %s\n", s2); // 段错误!
  9. return 0;
  10. }

  1. /** 函数指针 **/
  2. 方法一:
  3. int (*pfunc1) ( int, int);
  4. pfunc1 = add;
  5. pfunc1 ( 1, 2); // 3
  6. 方法二:
  7. typedef int (*PFUNC) (int, int);
  8. PFUNC pfunc2 = add;
  9. pfunc2 ( 10, 20); // 30
  10. 方法三: // 最难的用法
  11. (*(( int (*) ( int, int)) 0x0000000000400544)) ( 1000, 2000); // 3000
  12. /** 代码演示 - 函数指针、typedf、函数常地址 **/
  13. #include <stdio.h>
  14. int add (int x, int y) {
  15. printf ( "Enter add func...\n");
  16. return x + y;
  17. }
  18. // 声明一个指针变量pFunc
  19. // 该4字节存储函数的地址
  20. // 存 <返回值是int,参数为int,int类型的函数地址>
  21. typedef int (*pFunc) (int, int);
  22. int main (void) {
  23. int res = 0;
  24. // res = add (1, 2); 正常调用
  25. // 定义一个指针变量pfunc1
  26. // 这个变量是个指针,bit32-4字节
  27. // 该4字节存储1个函数的地址
  28. // 存 <返回值是int,参数为int,int类型的函数地址>
  29. int (*pfunc1) ( int, int);
  30. pfunc1 = add; // 函数名和数组名一样,可以代表其首地址
  31. res = pfunc1 ( 10, 20);
  32. printf ( "res = %d\n", res); // 30
  33. // ---------- typedef -----------
  34. pFunc pfunc2;
  35. pfunc2 = add;
  36. res = pfunc2 ( 100, 200);
  37. printf ( "res = %d\n", res); // 300
  38. // 函数指针最难的用法
  39. res = (*(( int (*) ( int, int)) 0x0000000000400544)) ( 1000, 2000);
  40. printf ( "res = %d\n", res); // 3000
  41. return 0;
  42. }

三、编写一个shell框架
  1. /** 代码演示 - 主函数 main.c **/
  2. /* 不需要写 include ,关键字 extern 可以实现跨文件使用函数。
  3. #include "uart.h"
  4. #include "led.h"
  5. #include "beep.h"
  6. #include "mystrcmp.h"
  7. */
  8. #define CMD_MAX_LED 32
  9. char cmd_buf[CMD_MAX_LED];
  10. int main (void) {
  11. // 8N1 115200 non-FIFO polling
  12. uart_init (); // uart 串口初始化
  13. led_init (); // led 初始化
  14. beep_init (); // beep 初始化
  15. while ( 1) {
  16. // 输出命令提示符
  17. uart_puts ( "\nmyArmShell#: ");
  18. // 接收用户输入的数据
  19. uart_gets (cmd_buf, CMD_MAX_LED);
  20. if (! mystrcmp (cmd_buf, "ledon")) {
  21. led_on ();
  22. uart_puts ( "\nledon success...");
  23. }
  24. if (! mystrcmp (cmd_buf, "ledoff")) {
  25. led_off ();
  26. uart_puts ( "\nledoff success...");
  27. }
  28. if (! mystrcmp (cmd_buf, "beepon")) {
  29. beep_on ();
  30. uart_puts ( "\nbeepon success...");
  31. }
  32. if (! mystrcmp (cmd_buf, "beepoff")) {
  33. beep_off ();
  34. uart_puts ( "\nbeepoff success...");
  35. }
  36. }
  37. return 0;
  38. }
  1. /** 代码演示 - uart.h **/
  2. #ifndef _UART_H_
  3. #define _UART_H_
  4. extern void uart_init (void);
  5. extern void uart_puts (char*);
  6. extern void uart_gets (char*, int);
  7. #endif //_UART_H_
  8. /** 代码演示 - uart.c **/
  9. #define UART0CLKENB *((volatile unsigned int*)0xc00a9000)
  10. #define UART0CLKGEN0L *((volatile unsigned int*)0xc00a9004)
  11. #define GPIOD_ALTFN0 *((volatile unsigned int*)0xc001d020)
  12. #define GPIOD_ALTFN1 *((volatile unsigned int*)0xc001d024)
  13. #define GPIOD_PULLENB *((volatile unsigned int*)0xc001d060)
  14. #define ULCON0 *((volatile unsigned int*)0xc00a1000)
  15. #define UCON0 *((volatile unsigned int*)0xc00a1004)
  16. #define UFCON0 *((volatile unsigned int*)0xc00a1008)
  17. #define UTRSTAT0 *((volatile unsigned int*)0xc00a1010)
  18. #define UTXH0 *((volatile unsigned int*)0xc00a1020)
  19. #define URXH0 *((volatile unsigned int*)0xc00a1024)
  20. #define UBRDIV0 *((volatile unsigned int*)0xc00a1028)
  21. #define UFRACVAL0 *((volatile unsigned int*)0xc00a102c)
  22. void uart_init (void) {
  23. /* uart0 clk disable */
  24. UART0CLKENB &= ~( 1 << 2);
  25. // GPIOD18(Tx 接收管脚) GPIOD14(Rx 发送管脚) 配置功能Function1
  26. GPIOD_ALTFN0 &= ~( 3 << 28); // GPIOD14
  27. GPIOD_ALTFN0 |= ( 1 << 28);
  28. GPIOD_ALTFN1 &= ~( 3 << 4); // GPIOD18
  29. GPIOD_ALTFN1 |= ( 1 << 4);
  30. // 时钟配置:选择PLL[1] 800MHz
  31. UART0CLKGEN0L &= ~( 7 << 2);
  32. UART0CLKGEN0L |= ( 1 << 2);
  33. // 分频设置 800/(0x0f+1)=50MHz
  34. UART0CLKGEN0L &= ~( 0xff << 5); // [12:5] 8个位
  35. UART0CLKGEN0L |= ( 0xf << 5); // [12:5] 4个位设置为1111
  36. // UART控制器设置
  37. ULCON0 = 0x03; // 8N1
  38. UCON0 = 0x05; // 0101 == 0x05 polling
  39. UFCON0 |= ( 3 << 1); // 清空FIFO,解决开发板命令行下运行有多余字符的bug
  40. UFCON0 &= ~( 1 << 0); // non-FIFO disable
  41. UBRDIV0 = 26; // 50000000/(115200*16) - 1 == 26.13
  42. UFRACVAL0 = 2; // 0.13*16 == 2.08
  43. /* uart0 clk enable */
  44. UART0CLKENB |= ( 1 << 2);
  45. }
  46. void uart_putc (char c) {
  47. // UTRSTAT0 bit[1] == 1, 缓存寄存器为empty
  48. // 轮询是否为空
  49. while (! (UTRSTAT0 & 0x02)); // 分析?
  50. UTXH0 = c;
  51. if (c == '\n')
  52. uart_putc ( '\r');
  53. }
  54. void uart_puts (char* str) {
  55. if (! str)
  56. return ;
  57. while (*str) {
  58. uart_putc (*str);
  59. str++;
  60. }
  61. }
  62. char uart_getc (void) {
  63. // 轮询 polling UTRSTAT0 bit[0] = 1 received data
  64. while (! (UTRSTAT0 & 0x01));
  65. return ( char)(URXH0 & 0xff); // 只取低8位,是有效数据
  66. }
  67. void uart_gets (char* buf, int len) {
  68. int i = 0;
  69. char tmp = 0;
  70. while (i < len - 1) {
  71. tmp = uart_getc ();
  72. // 回显,注释掉该句验证效果?
  73. uart_putc (tmp);
  74. buf[i] = tmp;
  75. if (tmp == '\r')
  76. break;
  77. i++;
  78. }
  79. // 添加字符串结束标志
  80. buf[i] = '\0';
  81. }
  1. /** 代码演示 - mystrcmp.h **/
  2. #ifndef _MYSTRCMP_H
  3. #define _MYSTRCMP_H
  4. int mystrcmp (const char*, const char*);
  5. #endif //_MYSTRCMP_H
  6. /** 代码演示 - mystrcmp.c **/
  7. #include "mystrcmp.h"
  8. int mystrcmp (const char* s1, const char* s2) {
  9. while (*s1) {
  10. if (*s1 > *s2)
  11. return 1;
  12. else if (*s1 < *s2)
  13. return -1;
  14. s1++;
  15. s2++;
  16. }
  17. return *s2 == 0 ? 0 : -1;
  18. }
  1. /** 代码演示 - led.h **/
  2. #ifndef _LED_H_
  3. #define _LED_H
  4. extern void led_text (void);
  5. extern void led_on (void);
  6. extern void led_off (void);
  7. #endif // _LED_H
  8. /** 代码演示 - led.c **/
  9. #define GPIOC_OUT *((volatile unsigned int*)0xc001c000)
  10. #define GPIOC_OUTENB *((volatile unsigned int*)0xc001c004)
  11. #define GPIOC_ALTFN0 *((volatile unsigned int*)0xc001c020)
  12. void led_init (void) {
  13. // 配置对应管脚为GPIO功能
  14. GPIOC_ALTFN0 &= ~ ( 3 << 24); // clear bit 24,25
  15. GPIOC_ALTFN0 |= ( 1 << 24); // set bit 24
  16. // 选择为输出功能
  17. GPIOC_OUTENB |= ( 1 << 12); // OUTPUT
  18. }
  19. void led_on (void) {
  20. GPIOC_OUT &= ~ ( 1 << 12); // clear bit 12
  21. }
  22. void led_off (void) {
  23. GPIOC_OUT |= ( 1 << 12); // set bit 12
  24. }
  1. /** 代码演示 - beep.h **/
  2. #ifndef _BEEP_H_
  3. #define _BEEP_H_
  4. extern void beep_init (void);
  5. extern void beep_on (void);
  6. extern void beep_off (void);
  7. extern void delay (unsigned int);
  8. #endif //_BEEP_H_
  9. /** 代码演示 - beep.c **/
  10. #define GPIOC_ALTFN0 *((volatile unsigned int*)0xc001c020)
  11. #define GPIOC_OUTENB *((volatile unsigned int*)0xc001c004)
  12. #define GPIOC_OUT *((volatile unsigned int*)0xc001c000)
  13. void beep_init (void) {
  14. // 配置GPIO管脚
  15. GPIOC_ALTFN0 &= ~( 3 << 28);
  16. GPIOC_ALTFN0 |= ( 1 << 28);
  17. // 设置输出功能
  18. GPIOC_OUTENB |= ( 1 << 14);
  19. }
  20. void beep_on (void) {
  21. // while (1) { // 暂无法中断,故先注销
  22. GPIOC_OUT |= ( 1 << 14); // 鸣叫
  23. // delay (10000000);
  24. // GPIOC_OUT &= ~(1 << 14); // 不叫
  25. // delay (10000000);
  26. // }
  27. }
  28. void beep_off (void) {
  29. GPIOC_OUT &= ~( 1 << 14);
  30. }
  31. void delay (unsigned int n) {
  32. while (--n);
  33. }

自我验证补充:
  1. "extern - 跨文件调用函数 "
  2. // add.h
  3. #ifndef _ADD_H
  4. #define _ADD_H
  5. extern int add (int, int);
  6. #endif //_ADD_H
  7. // add.c
  8. int add (int x, int y) {
  9. return x + y;
  10. }
  11. // main.c
  12. #include <stdio.h>
  13. int main (void) {
  14. int i = 10;
  15. int j = 20;
  16. int sum = add (i, j);
  17. printf ( "sum = %d\n", sum);
  18. return 0;
  19. }
  1. " static - 静态局部变量 "
  2. // 数据段,作用域:当前函数,生命周期:整个当前程序。
  3. #include <stdio.h>
  4. int add (int x, int y) {
  5. static int sum;
  6. return x + y;
  7. }
  8. int* add1 (int x, int y) {
  9. static int s = 1; // static int s = x + y; 错误:初始值设定元素不是常量
  10. printf ( "&s is : %p\n", &s); // 0x601020
  11. printf ( "s is : %d\n", s); // 1
  12. return &s;
  13. }
  14. int main (void) {
  15. int* p_num = add1 ( 10, 20); // s is : 1
  16. printf ( "p_num is : %p\n", p_num); // 0x601020
  17. printf ( "*p_num is : %d\n", *p_num); // 1
  18. *p_num = 2;
  19. add1 ( 11, 22); // s is : 2
  20. // printf ("&sum is : %p", &sum); // sum 未声明
  21. return 0;
  22. }

猜你喜欢

转载自blog.csdn.net/qq_35769746/article/details/80937309