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);
}
}