CS61C Lab2

覆盖广的一个lab

Exercise 0: Makefiles

  1. Which target is part of a rule that deletes all the compiled programs?
  2. Which target is part of a rule that makes all the compiled programs?
  3. Which compiler is currently being used?
  4. What C standard are we currently using?
  5. How would we reference a variable FOO in a makefile?
  6. What operating system does the term “Darwin” represent?
  7. What line creates the lfsr program from its object files? (Give its line number.)

我的答案:

1
2
3
4
5
6
7
1.clean
2.all
3.gcc
4.c99
5.$(Foo)
6.macos
7.31

Exercise 1: Bit Operations

get_bit很简单,set_bit中需要将v01减去1构造一下变成全0和全1来判断条件, 做过csapp的datalab之后一下就有思路了。若v1则只需要移动n个bit到对应的bit或一下就行了,置为0则需要考虑一下除了移动到的bit之外的bit都置为1,可以在移位前做加上一个1的处理,随后移动到相应位置之后再取反,与一下即可。flip_bit只需要稍加处理在应用set_bit的模式即可,先取到即将要翻转的bit,随后与1做异或运算,使得0变为1,1变为0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h>
#include "bit_ops.h"

// Return the nth bit of x.
// Assume 0 <= n <= 31
unsigned get_bit(unsigned x,
unsigned n) {
// YOUR CODE HERE
// Returning -1 is a placeholder (it makes
// no sense, because get_bit only returns
// 0 or 1)
return (x >> n) & 0x1;
}
// Set the nth bit of the value of x to v.
// Assume 0 <= n <= 31, and v is 0 or 1
void set_bit(unsigned * x,
unsigned n,
unsigned v) {
// YOUR CODE HERE
int flag = v-1;
(*x) = (((*x) | (v << n)) & (~flag)) | ((*x) & (~((v+1) << n)) & flag);
}
// Flip the nth bit of the value of x.
// Assume 0 <= n <= 31
void flip_bit(unsigned * x,
unsigned n) {
// YOUR CODE HERE
int v = ((*x) >> n) & 0x1;
v ^= 1;
int flag = v-1;
(*x) = (((*x) | (v << n)) & (~flag)) | ((*x) & (~((v+1) << n)) & flag);
}

Exercise 2: Linear Feedback Shift Register

Linear Feedback Shift Register

根据图中信息构造门级电路即可,test中循环已经写好不需要再构建循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "lfsr.h"

void lfsr_calculate(uint16_t *reg) {
/* YOUR CODE HERE */
uint16_t regs = *reg;
uint16_t msb = ((regs&0x1)^((regs>>2)&0x1)^((regs>>3)&0x1)^((regs>>5)&0x1))<<15;
(*reg) >>= 1;
(*reg) |= msb;
}

Exercise 3: Linked Lists

面试常考的翻转链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "list.h"

/* Add a node to the end of the linked list. Assume head_ptr is non-null. */
void append_node (node** head_ptr, int new_data) {
/* First lets allocate memory for the new node and initialize its attributes */
/* YOUR CODE HERE */
node* new_node = (node*)malloc(sizeof(node));
new_node->val = new_data;

/* If the list is empty, set the new node to be the head and return */
if (*head_ptr == NULL) {
/* YOUR CODE HERE */
*head_ptr = new_node;
return;
}
node* curr = *head_ptr;
while (curr->next != NULL) {
curr = curr->next;
}
/* Insert node at the end of the list */
/* YOUR CODE HERE */
curr->next = new_node;
new_node->next = NULL;
}

/* Reverse a linked list in place (in other words, without creating a new list).
Assume that head_ptr is non-null. */
void reverse_list (node** head_ptr) {
node* prev = NULL;
node* curr = *head_ptr;
node* next = NULL;
while (curr != NULL) {
/* INSERT CODE HERE */
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
/* Set the new head to be what originally was the last node in the list */
*head_ptr = prev;
}

Exercise 4: Memory Management

vectorMakefile中指定目标, 将下列语句添加到Makefile中。

1
2
$(VECTOR_PROG): $(VECTOR_OBJS)
$(CC) $(CFLAGS) -g -o $(VECTOR_PROG) $(VECTOR_OBJS) $(LDFLAGS)

分析一下: bad_vector_new。声明的指针变量v未被使用,且没有为结构体vector_t动态开辟堆空间,而是使用野指针,这样很危险; also_bad_vector_new。声明栈上的结构体数据,最后返回之后拷贝的开销非常大,因此也不够合理, 且如果未及时对返回值进行保存,栈帧会有被覆盖的可能,会丢失返回的数据。

$ valgrind –tool=memcheck –leak-check=full –track-origins=yes [OS SPECIFIC ARGS] ./<executable>

  • Valgrind默认使用memcheck工具。还有其它工具包括: Callgrind, Cachegrind, Helgrind, Massif
  • --leak-check=full: 详细地显示每个单独的内存泄露。
  • --track-origins=yes: 这会跟踪初始化值的来源。着重于有用的输出而不是速度。

注意在函数vector_set的题意,超出访问的loc超出堆内存的范围需要重新再申请空间realloc,而不是简单地提示错误信息,若重新申请为空则提示错误信息allocation_failed。用cgdb慢慢调就行了。在make vector_test通过后,使用工具Valgrind检测到内存泄露,查看了一下vector_test函数,发现存在使用未初始化元素的行为,随后便可以将问题可以定位到vector_set函数中,在其中加入for循环初始化即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/* Create a new vector with a size (length) of 1
and set its single component to zero... the
RIGHT WAY */
vector_t *vector_new() {
/* Declare what this function will return */
vector_t *retval;

/* First, we need to allocate memory on the heap for the struct */
retval = (vector_t*)malloc(sizeof(vector_t));/* YOUR CODE HERE */

/* Check our return value to make sure we got memory */
if (retval == NULL/* YOUR CODE HERE */) {
allocation_failed();
}

/* Now we need to initialize our data.
Since retval->data should be able to dynamically grow,
what do you need to do? */
retval->size = 1;/* YOUR CODE HERE */;
retval->data = (int*)malloc(sizeof(int)*retval->size);/* YOUR CODE HERE */;

/* Check the data attribute of our vector to make sure we got memory */
if (retval->data == NULL/* YOUR CODE HERE */) {
free(retval); //Why is this line necessary? Because it allocate the memory of vector but not allocate the data that will cause memory leak.
allocation_failed();
}

/* Complete the initialization by setting the single component to zero */
/* YOUR CODE HERE */
*(retval->data) = 0;

/* and return... */
return retval;
}

/* Return the value at the specified location/component "loc" of the vector */
int vector_get(vector_t *v, size_t loc) {

/* If we are passed a NULL pointer for our vector, complain about it and exit. */
if(v == NULL) {
fprintf(stderr, "vector_get: passed a NULL vector.\n");
abort();
}

/* If the requested location is higher than we have allocated, return 0.
* Otherwise, return what is in the passed location.
*/
if (loc < v->size/* YOUR CODE HERE */) {
return v->data[loc];/* YOUR CODE HERE */
} else {
return 0;
}
}

/* Free up the memory allocated for the passed vector.
Remember, you need to free up ALL the memory that was allocated. */
void vector_delete(vector_t *v) {
/* YOUR SOLUTION HERE */
free(v);
}

/* Set a value in the vector. If the extra memory allocation fails, call
allocation_failed(). */
void vector_set(vector_t *v, size_t loc, int value) {
/* What do you need to do if the location is greater than the size we have
* allocated? Remember that unset locations should contain a value of 0.
*/

/* YOUR SOLUTION HERE */
if (loc >= v->size) {
v->data = (int*)realloc(v->data, sizeof(int)*(loc+1));
if (v->data == NULL) {
allocation_failed();
}
for (size_t i = v->size; i < loc+1; ++i)
v->data[i] = 0;
v->size = loc+1;
}
v->data[loc] = value;
}