int *num_array; int **char_array; num_array = (int *)malloc(sizeof(int) * 15); char_array = (char **)malloc(sizeof(char *) * 10); char_array[3] = (char *)malloc(sizeof(char) * 30);malloc returns the decimal addresses of 100, 300, and 500 respectively.
address = char_array[3] + 2 = 500 + 2(1) = 502
char **array; array = (char **)malloc(sizeof(char *) * 20); strcpy(array[5], "brad");Solution: The final statement is invalid because array[5] does not point to a legitimate memory address. The malloc on the previous line allocates a memory block that can hold 20 character pointers but it does not initialize these pointers. After the malloc, the block of memory assigned to array looks like:
array ----> ----- | ? | ----- | ? | ----- | ? | ----- |...| ----- | ? | -----The statement would have been ok if it had been written as:
array[5] = strdup("brad");In this case the strdup would have allocated enough memory to hold the string "brad."
char *array; array = (char *)malloc(sizeof(char) * 300);If you think it is valid, write two statements that assign:
letter = 'a';I want to see the array notation you would use to access the third character.
strcpy(array[50], "brad"); letter = array[52]but this notation is awkward and prone to error. When I have an array of strings I want to be able to access it using the following, more natural notation:
strcpy(array[5], "brad"); letter = array[5][2];In order to use the array in this manner, I would have needed to use the following set of statements to create the array:
char **array; // declare an array of char * pointers // get storage for 30 string pointers array = (char **)malloc(sizeof(char *) * 30); // allocate 10 character memory blocks for each of the // pointers to point to for (i = 0; i < 30; i++) array[i] = (char *)malloc(sizeof(char) * 10);
typedef struct { // this struct is an anonymous struct double gross_pay; char last_name[20]; int age; } student;
student *new_student;
new_student = (student *)malloc(sizeof(student));
char *name;Solution:
printf("%-20s", name);
struct employee { int age; char last_name[20]; } struct employee *a; int *b; char name[20]; char *save_name; void *void_ptr;Place a check mark next to any line you think might cause a compiler error and explain why. Place an X next to any line that you think could prove problematic at runtime and explain why. You may assume that all variables that are assigned to another variable have been given malloc'ed pieces of memory.
void_ptr = (void *)b; Not only is the statement correct but it uses good form by type casting b to be a void *. void_ptr = (void *)name; save_name = (char *)void_ptr; Another perfect set of statements. name is properly cast to a (void *) and then the (void *) is cast back to a (char *) for assignment to save_name. _/ void_ptr = (void *)a; void_ptr->age = 30; You know that the void_ptr points to an employee struct but the compiler is not that smart. To the compiler a (void *) is a generic pointer and you cannot access any field through a void pointer. You would have to cast the void_ptr back to an employee struct in order to be able to access the age field. X void_ptr = (void *)b; name = (char *)void_ptr; The compiler is happy because you have cast the pointers to the right types. However the second assignment makes no sense. You are assigning an integer pointer to a variable that expects a character pointer. When the program tries to interpret name as a character string, it will get garbage. void_ptr = a; A trick question of sorts. Good form dictates that you cast a to a (void *) before assigning it to void_ptr but the compiler will accept the above statement and your program will work perfectly at run-time. The rationale for the compiler accepting the statement is that you are assigning a restricted type, an (int *), to a less restricted type, a (void *) or more generally, a generic pointer. If you want an analogy, think of assigning an integer to a floating point variable. You do not have to perform a cast because you are assigning a more restricted type, an int, to a less restricted type, a floating point number. In doing so you lose no precision. The same can be said of assigning a typed pointer to a void pointer.
#include <stdio.h> #include <string.h> #include "tg.h" // define the extent of a line here so that it can be easily changed // in the future #define MARGIN 80 main() { TokenGen *tg; char *token; int col = 0; // open a token generator that reads from stdin and read the // first token tg = new_tokengen(0); token = tokengen_get_token(tg); while(token != 0) { // if the token is <p> start a new paragraph if (strcmp(token, "<p>") == 0) { printf("\n\n"); col = 0; } // do not print a space before the first token on a line else if (col == 0) { printf("%s", token); col = strlen(token); } else { // before printing the token on the current line make // sure that it can fit on the current line. The reason // we add 1 to strlen(token) is because we must include // the space between the previous word and token col += (1 + strlen(token)); if (col > MARGIN) { // if the token needs to be printed on the next // line, start a new line but do not read a new // token since we still need to process this token // in the next loop iteration. col = 0; printf("\n"); continue; // don't read another token } else { printf(" %s", token); } } token = tokengen_get_token(tg); } }