week 4 Memory
十六进制
介绍十六进制:两位数的十六进制 FF,表示十进制的 255
为什么要使用十六进制? 可以表示得更简洁
eg:
二进制 11111111 -> 十六进制 0xFF
#include <stdio.h>
int main(void)
{
int n = 50;
printf("%i\n", n);
}
// 50 被记录在电脑的内存中,程序员通过 n 可以访问
// 1 个整数用 4 个 bites 表示
两个新的运算符
-
&:在变量前加,可以得到存储变量的内存地址
int n = 50; printf("%p\n", &n);
-
*:有两个作用
- 在声明变量时使用,代表这个变量存储的是相应的内存地址
- 在使用该变量时加上,则代表指向该内存地址中的值
int n = 50; int *p = &n; // 存储内存地址 printf("%i\n", *p); // 去p存储的地址
指针
- 含义:指针是计算机内存中某个东西的地址
- 表示方式:int/string… *p ;叫做指针 p,p是变量名
- 指针在内存中是 8 位
- 字符串变量存储的是字符串第一个字符的地址
指针的运算
在 C 中并没有 string 这一结构类型,是在 cs50.h 的头文件中定义的,根据字符串变量存储的是字符串第一个字符的地址这一特性
typedef char *string;
char *s = "HI!"; // 指针 s 存储的是地址
printf("%p\n", s); // 打印存储的地址
printf("%s\n", s); // printf 可以将指向地址的所有内容转换出来,所以可以不用 *s(打印单个字符)
printf("%c\n", *s); // H
printf("%c\n", *(s+1)); // I
printf("%c\n", *(s+2)); // !
字符串比较
为什么字符串判断相等不能用 == 而需要使用 strcmp 函数进行比较?
== 是比较两边内存地址,而不是逐个进行字符比较
其余的数据类型可以使用 == 比较,字符串是特殊的,设计如此
字符串的复制
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
// Get a string
string s = get_string("s: ");
// Copy string's address
string t = s;
// Capitalize first letter in string
//t[0] = toupper(t[0]);
if (strlen(t) > 0)
{
t[0] = toupper(t[0]);
}
// Print string twice
printf("s: %s\n", s);
printf("t: %s\n", t);
}
为什么结果会是如此?
因为复制的字符串的内存地址,使得 t 和 s 指向相同的内容
正确的复制(使用两种新的函数):
- malloc:分配一定数量的内存地址
- free:释放被分配的内存
如果不将最后的 NULL 复制过来,在打印的时候会在最后一直打印随机字符,直到出现 NULL 后才会停止
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
// Get a string
string s = get_string("s: ");
// Copy string's address
int l = strlen(s);
// 分配内存地址
// 加1 是因为需要最后一个 NUL 字符
string t = malloc(l + 1);
// 复制里面的字符串,NUL 也需要复制
// for(int i = 0, n = strlen(s); i < n; i++)
// for (int i = 0; i < l+1; i++)
// {
// t[i] = s[i];
// }
strcpy(t, s);
// Capitalize first letter in string
if (strlen(t) > 0)
{
t[0] = toupper(t[0]);
}
// Print string twice
printf("s: %s\n", s);
printf("t: %s\n", t);
}
复制程序优化:
- 当输入的字符串是无限长时,get_string 会返回 NULL(代表地址0),所以要检查返回值是否为 NULL
- malloc 同理
- 在使用 malloc 分配内存之后,使用完成后一定要记得将分配的内存释放
#include <cs50.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
// Get a string
string s = get_string("s: ");
if (s == NULL)
{
return 1;
}
// 分配内存地址
// 加1 是因为需要最后一个 NULL 字符
string t = malloc(strlen(s) + 1);
if (t == NULL)
{
return 1;
}
// 复制里面的字符串,NULL 也需要复制
strcpy(t, s);
// Capitalize first letter in string
if (strlen(t) > 0)
{
t[0] = toupper(t[0]);
}
// Print string twice
printf("s: %s\n", s);
printf("t: %s\n", t);
// 释放
free(t);
return 0;
}
Valgrind
检查内存泄露的方法:
编译运行之后,使用 valgrind ./memory
垃圾数据
当声明了某个变量,但又没有进行赋值的时候,C 会产生一些随机的值,有可能是先前的程序用过的;所以声明变量之后最好进行赋值
交换函数
#include<stdio.h>
void swap(int a, int b);
int main()
{
int x = 1;
int y = 2;
printf("x is %i, y is %i\n", x, y);
swap(x, y);
printf("x is %i, y is %i\n", x, y);
}
void swap(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
这个版本的代码为什么无法成功交换?
修改代码如下:
#include<stdio.h>
void swap(int *a, int *b);
int main()
{
int x = 1;
int y = 2;
printf("x is %i, y is %i\n", x, y);
// 需要传入的也是内存地址,而不是相应的值
swap(&x, &y);
printf("x is %i, y is %i\n", x, y);
}
// 接受的参数是内存地址
void swap(int *a, int *b)
{
// 这里 *a *b 的值
int tmp = *a;
*a = *b;
*b = tmp;
}
溢出
- A heap overflow is when you overflow the heap, touching areas of memory you are not supposed to.
- A stack overflow is when too many functions are called, overflowing the amount of memory available.
- Both of these are considered buffer overflows.
输入函数
get_int:
#include<stdio.h>
int main()
{
int x;
printf("x: ");
scanf("%i", &x);
printf("x:%i\n", x);
}