So i'm writing a C program for a simple shell. Not too difficult but I've run into one really odd problem that I can't explain. I'm attempting to create a two dimensional array within a struct to represent a command and its arguments. For the command "ls -l" for example i'd like to have the first element "ls" and the second "-l". Seems to work fine other than that malloc changes the "ls" to "ms". The same thing for other commands, the first character is incremented; not before the malloc and immediately afterward.
This is the hunk of code in question....
printf ("PRE MALLOC: %c\n", ret_val->args[0][0]);
printf ("[0] %p\n", ret_val->args[0]);
ret_val->args[1] = (char*) malloc ((3) * sizeof (char));
printf ("[0] %p\n", ret_val->args[0]);
printf ("[1] %p\n", ret_val->args[1]);
printf ("POST MALLOC: %c\n", ret_val->args[0][0]);
All I'm attempting to accomplish is to allocate a 3 ( -l + the null ) character array to hold the "-l" in args[1]. These aren't really going to be hardcoded either but I figured it makes the point better.
The output produced it this...
PRE MALLOC: l
[0] 80613b0
[0] 80613b0
[1] 80613b8
POST MALLOC: m
So the two addresses don't overlap or anything strange but the first character of the first array is incremented? I'm sorry if I'm overlooking something stupid. But I can't think of any reason why this would happen.
Any ideas?
Thanks,
Here is much more code, for some context.
int lcv;
int numargs;
int numchars;
int offset;
int bool;
TCommand* ret_val;
char** tmp;
ret_val = (TCommand*) malloc ( sizeof (TCommand) );
ret_val->cmd = NULL;
ret_val->args = NULL;
/* CMD */
lcv = 0;
numargs = 0;
numchars = 0;
offset = 0;
/* Remove initial whitespace */
while (input[offset] == ' ')
{
++offset;
}
lcv = offset;
/* Loop through command string */
while ( input[lcv] != ' ' && input[lcv] != 0 && input[lcv] != '&')
{
++numchars;
++lcv;
}
ret_val->cmd = (char*) malloc ( (numchars+1) * sizeof(char));
/* Copy to command string */
memcpy (ret_val->cmd, &(input[offset]), (numchars * sizeof (char)));
ret_val->cmd[numchars] = 0;
offset += numchars;
/* Copy command string into first argument */
ret_val->args = (char**) malloc ( sizeof (char*));
memcpy (ret_val->args[numargs++],ret_val->cmd, (numchars+1) * sizeof(char));
bool = 1;
while ( bool )
{
/* Remove initial whitespace */
while (input[offset] == ' ')
{
++offset;
}
lcv = offset;
if ( input[lcv] == 0 )
{
bool = 0;
}
else
{
++numargs;
tmp = (char**) realloc (ret_val->args, numargs * sizeof (char*));
ret_val->args = tmp;
numchars = 0;
while ( input[lcv] != ' ' && input[lcv] != 0 &&
input[lcv] != '&')
{
++numchars;
++lcv;
}
printf ("PRE MALLOC: %c\n", ret_val->args[0][0]);
printf ("[0] %p\n", ret_val->args[0]);
ret_val->args[1] = (char*) malloc ((2) * sizeof (char));
printf ("[0] %p\n", ret_val->args[0]);
printf ("[1] %p\n", ret_val->args[1]);
printf ("POST MALLOC: %c\n", ret_val->args[0][0]);
fflush(stdout);
memcpy (ret_val->args[numargs-1],&(input[offset]),numchars * sizeof (char));
ret_val->args[numargs-1][numchars] = 0;
offset += numchars;
}
}
This code:
/* Copy command string into first argument */
ret_val->args = (char**) malloc ( 2 * sizeof (char*));
memcpy (ret_val->args[numargs++],ret_val->cmd, (numchars+1) * sizeof(char));
Copies through the uninitialised pointer ret_val->args[0]. Try:
/* Copy command string into first argument */
ret_val->args = malloc(2 * sizeof ret_val->args[0]);
ret_val->args[numargs] = malloc(numchars + 1);
memcpy(ret_val->args[numargs++], ret_val->cmd, numchars + 1);
(Note that sizeof(char) is defined to be 1 by the language).
The problem isn't in the fragment you show; it is somewhere in the code you don't show.
So, you need to build your example from the ground up - showing the structure declarations, the memory management and copying you do for the structures.
I guess we need to see how args[0] is allocated, and how it is given a value.
I can make a few suggestions, though they won't fix the allocation problem:
It is not necessary to cast the result of malloc(), and while one has limited ability to change a type in an already-written C program, there is no reason to deliberately lower the level of abstraction by open-coding duplicate type information. Old texts and old code examples often cast malloc() because C did not always have a void * type, and even after it arrived people wrote in the old style by habit and for code portability to old compilers. But you are unlikely these days to ever need to compile for the PDP-11.
sizeof(char) is, by C99, 1, so it's OK to just say malloc(3), and it reads slightly easier because it's shorter. And if you know it's "-l", you may as well just use "-l".
This sounds like a classic example of heap corruption.
What you are seeing is most probably the result of the malloc() call overwriting the string that args[0] points to with its return value. This can happen if the args array does not actually have enough space for the second argument. By writing into args[1] you are overwriting the contents of the string that is pointed to by argv[0] and that I suspect you allocate with an strdup() call or similar immediately after allocating the args array.
Since the heap grows upwards, and assuming no intervening free() calls, by overstepping the bounds of a dynamically allocated array there is an increased chance of messing with the area allocated immediately afterwards.
I'll hazard a guess and recommend that you check the allocated size for the args array by printing out the arguments for the realloc() call and checking its return value.
EDIT:
Apparently not in this case. It seems that the argv[0] pointer could actually be uninitialised, without causing a segmentation fault. Interesting...
Related
I have a program that reads chars into a dynamic string buffer. We do not know the size of the string, and it is a requirement that we do not simply set a fixed-size, "large-enough" buffer.
The relevant function works like this:
char* read_field(FILE* data)
{
int size = 8;
char *field = malloc(size);
if (field == NULL)
exit(1);
char *tmp = NULL;
int idx = 0;
int ch = EOF;
while (ch) {
ch = fgetc(data);
// Double size if full
if (size <= idx) {
size *= 2;
tmp = realloc(field, size);
if (!tmp)
exit(1);
field = tmp;
}
field[idx++] = ch;
// Relevant termination in my use case
if (ch == ';' || ch == '\n')
ch = 0;
}
printf("field: %s\n"); // value correct, but sometimes valgrind error
return field; // field is free'd by the caller
}
Now the program seems to work, but when running it through Valgrind I get the errors Uninitialised value was created by a heap allocation and Conditional jump or move depends on uninitialised value(s). These error appears arbitrarily (sometimes) when I call functions like printf or strlen, as seen in the code above.
This problem is sorted if I use calloc instead of malloc / realloc, but then the reallocation process becomes messier.
Is the Valgrind error something that could be ignored if the program works fine? What are the implications of not initializing the memory to zero? If this can't be ignored, what's the best design to sort it out?
You should put a string terminator at the end of the string.
PS:
If you want to clear some memory use memset, it's faster than a for cycle
use calloc , its much better than malloc and memset.
Example
char *string = calloc( 100 , sizeof(char*));
// Calloc automatically fills the memory blocks
// Its much faster than malloc and memset
// In addition , only in C you don't need typecast for memory allocators
So I'm writing in c and i have an array of character pointers which i'm using as dynamic arrays (char *chat[CHAT_ROWS]), I also have a function to copy one array of characters into another array of characters (below), the specific call to Copy_String() which produces an error uses chat[CHAT_ROWS - 1] as string_out.
I get the error "corrupted double-linked list" when I try and feed string_in an array which is over 30 characters long. The error appears to be coming from the second call to realloc() between high and mid.
void Copy_String(char *string_out, char *string_in)
{
int i;
printf("first\n");
string_out = (char *)realloc(string_out, 0);
i = 0;
while(string_in[i] != '\0')
{
printf("high\n");
string_out = (char *)realloc(string_out, i * sizeof(char));
printf("mid\n");
string_out[i] = string_in[i];
printf("low\n");
i += 1;
}
string_out = (char *)realloc(string_out, i * sizeof(char));
string_out[i] = '\0';
printf("end\n");
return;
}
If you realloc a string pointer parameter in a function, you MUST return it. If you do not, the argument passed in is quite likely to be invalid after the function returns.
This is a quite slow method for copying the string, as you go allocating memory before copying every char.
Anyway, your issue is here:
string_out = (char *)realloc(string_out, i * sizeof(char));
Notice that in the first interaction, "i" will be zero, so you are going to allocate "0 bytes", resulting in null. In the end, you write to null.
So replace the code with:
string_out = (char *)realloc(string_out, (i + 1) * sizeof(char));
Should work.
Also, as noted by others, you should return string_out to the caller, otherwise, you will loose all the work.
My code works fine, but I am receiving valgrind errors. I want to know how to correct my code to in respect to using these malloc and free statements correctly with the char * * dest. Please don't tell me not to malloc and free unless I am doing it at the incorrect locations. Having either or both the corrected code for strcat_ex in answer03.c or an explanation of my misunderstanding of malloc, free, and initialization after malloc would be greatly appreciated. I apologize in advance for the long post, but I wanted to provide everything necessary.
More information: I am mainly focusing on the method strcat_ex (this is not the same as strncat -- read the description of the function to see the difference with int *n). The problem occurs with the fact that I need to remalloc the parameter memory for the string (char *) in dest (char **) and if it doesn't have enough space allocated with it and after I malloc it is not initialized. This doesn't make sense to me how to initialize "heap" memory after a malloc. I didn't believe initialization had to happen after a malloc.
Note: pa03.c and answer03.h should not be changed at all.
Here is the relevant valgrind error (memcheck.log):
==28717== 1 errors in context 7 of 10:
==28717== Conditional jump or move depends on uninitialised value(s)
==28717== at 0x402D09C: strcat (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==28717== by 0x8048F98: strcat_ex (answer03.c:29)
==28717== by 0x8048631: main (pa03.c:16)
==28717== Uninitialised value was created by a heap allocation
==28717== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==28717== by 0x8048F46: strcat_ex (answer03.c:21)
==28717== by 0x8048631: main (pa03.c:16)
==28717==
==28717== ERROR SUMMARY: 10 errors from 10 contexts (suppressed: 0 from 0)
Lines referenced:
Line 16 (from pa03.c) SHOULD NOT BE CHANGE. Serves as an example of a call to the method parameters def, n, src, and return variable result are declared below in pa03.c:
result=strcat_ex(&dest, &n, src);
Line 21 (from answer03.c):
char * buffer = malloc(1 + 2 * (sizeOfDest + strlen(src)));
Line 29 (from answer03.c):
buffer = strcat(buffer,src);
Here is the relevant source code. This is where the valgrind error is and stackoverflow knowledge is needed (answer03.c):
Edit: Comments have been added and lines have been commented out to remove an error of mine in the code that had nothing to do directly with my question. I apologize for these heinous mistakes, but left the lines in there to help future readers understand.
#include "answer03.h"
#include <string.h>
char * strcat_ex(char * * dest, int * n, const char * src)
{
//Edit: Removed Line Below - Irrelevant variable resplaced with *n
//int sizeOfDest;
if(*dest == NULL)
{
*n = 0;
}
else
{
//Edit: Removed Line Below - variable replaced with *n
//sizeOfDest = strlen(*dest);
}
//Edit: Removed Line Below
//if(*dest != NULL && sizeOfDest >= 1 + sizeOfDest + strlen(src))
//Edit: Corrected Line
if(*dest !=NULL && *n >= 1 + strlen(*dest) + strlen(src))
{
strcat(*dest, src);
}
else
{
//Edit: *n replaced sizeOfDest and changes needed to be made to reflect this. Commented out lines were incorrect and irrelevant. Lines directly below them are the corrected versions, until you reach the next blank line
//*n = 1 + 2 * (sizeOfDest + strlen(src));
if(*dest != NULL)
*n = 1 + 2 * (strlen(*dest) + strlen(src));
else
*n = 1 + 2 * strlen(src);
//char * buffer = malloc(1 + 2 * (sizeOfDest + strlen(src)));
char * buffer = malloc(sizeof(char) * *n);
if(*dest != NULL)
{
strcpy(buffer, *dest);
free(*dest);
}
*dest = malloc(sizeof(buffer));
buffer = strcat(buffer,src);
*dest = buffer;
}
return *dest;
}
EVERYTHING BELOW THIS POINT SHOULD REMAIN UNCHANGED AND IS KNOWN TO BE CORRECT:
My compile statement (Makefile):
gcc -Wall -Wshadow -g pa03.c answer03.c -o pa03
My valgrind statement (Makefile):
valgrind --tool=memcheck --leak-check=full --verbose --track-origins=yes --log-file=memcheck.log ./pa03
Here is the function definition for strcat_ex (answer03.h):
#ifndef PA03_H
#define PA03_H
#include <stdlib.h>
/**
* Append the C-string 'src' to the end of the C-string '*dest'.
*
* strcat_ex(...) will append the C-string 'src' to the end of the string
* at '*dest'. The parameter 'n' is the address of a int that specifies how
* many characters can safely be stored in '*dest'.
*
* If '*dest' is NULL, or if '*dest' is not large enough to contain the result
* (that is, the sum of the lengths of *dest, src, and the null byte), then
* strcat_ex will:
* (1) malloc a new buffer of size 1 + 2 * (strlen(*dest) + strlen(src))
* (2) set '*n' to the size of the new buffer
* (3) copy '*dest' into the beginning of the new buffer
* (4) free the memory '*dest', and then set '*dest' to point to the new buffer
* (5) concatenate 'src' onto the end of '*dest'.
*
* Always returns *dest.
*
* Why do we need to pass dest as char * *, and n as int *?
* Please see the FAQ for an answer.
*
* Hint: These <string.h> functions will help: strcat, strcpy, strlen.
* Hint: Leak no memory.
*/
char * strcat_ex(char * * dest, int * n, const char * src);
//...
Here is the relevant code that calls source as a test (pa03.c):
#include <stdio.h>
#include <string.h>
#include "answer03.h"
int main(int argc, char **argv)
{
char * src;
char * dest;
char * result;
int n;
src="World!";
dest=NULL;
result=strcat_ex(&dest, &n, src);
printf("src=\"World!\";\ndest=NULL;\nstrcat_ex(&dest, &n, src);\n --> gives %s with n=%d\n",result,n);
result=strcat_ex(&dest, &n, "");
printf("Then strcat_ex(&dest, &n, \"\") yields --> gives %s with n=%d\n",result,n);
strcpy(dest,"abc");
result=strcat_ex(&dest, &n, "def");
printf("Then strcpy(dest,\"abc\"); strcat_ex(&dest, &n, \"def\") yields --> gives %s with n=%d\n",result,n);
free(dest);
//...
Here is the relevant output (print statments from pa03.c):
Note this is correct output (which my current code is able to produce).
src="World!";
dest=NULL;
strcat_ex(&dest, &n, src);
--> gives World! with n=13
Then strcat_ex(&dest, &n, "") yields --> gives World! with n=13
Then strcpy(dest,"abc"); strcat_ex(&dest, &n, "def") yields --> gives abcdef with n=13
//...
Last words:
I have attached the files needed to compile this code as well as the valgrind error log in linux using gcc and valgrind. There is more in the valgrind, but I posted what I believe to be most relevant. Thanks in advance.
Zip including all files:
http://www.filedropper.com/files_11
Your current function is utterly broken. It contains logic that cannot possible see fruition, at least one memory leak, and unchecked concatenation to a target buffer that is uninitialized. Among the things wrong, which are numerous:
Assuming sizeofDest dictates not just the storage of the current target string, but also the capacity of any concatenation operation. This is completely wrong, and is why n is provided to this function.
Outright memory leak: *dest = malloc(sizeof(buffer)); not only allocates a completely incorrect size of memory (the size of a pointer; not what it points to), it summarily leaks said allocation just two lines later.
Dead-code boolean logic: Given any non-negative value N, the expression N >= N + 1 + M, where M is a non-negative value, is impossible to ever be true.
You never use the key piece of information provided in tandem with the target pointer-by-address: the current target buffer size provided by n. That value is critical to this algorithm, as it is what dictates the real size of the target buffer, and in conjunction with the current string length in *dest*, will ultimately dictate whether a resize is required.
This is one way to do this function correctly:
char *strcat_ex(char ** dest, int * n, const char * src)
{
size_t dst_len = 0, src_len = strlen(src);
// determine current string length held in *dest
if (*dest && **dest)
dst_len = strlen(*dest);
size_t req_len = dst_len + src_len + 1;
// is space already available for the concatination?
if (*dest && *n >= req_len)
{
// we already know where the target address of the
// concatination is, and we already know the length of
// what is being copied. just copy chars.
if (src_len)
memcpy(*dest+dst_len, src, src_len+1);
}
else
{
// resize is required
void *tmp = realloc(*dest, req_len);
if (tmp != NULL)
{
// resize worked, original content of *dest retained, so
// we can once again simply copy bytes.
*dest = tmp;
memcpy(*dest+dst_len, src, src_len+1);
*n = (int)req_len;
}
else
{
perror("Failed to resize target buffer");
exit(EXIT_FAILURE);
}
}
return *dest;
}
I take great issue with the author of this design in that they chose int for the variable that holds the magnitude of the destination buffer. That magnitude can clearly never be negative, and it makes no sense to use anything other than the same type used by all standard library size-operations, size_t. I'd bring that to the attention of whoever designed this.
Simple Test
int main()
{
char *dst = NULL;
int n = 0;
strcat_ex(&dst, &n, "some string");
printf("%s : %d\n", dst, n);
strcat_ex(&dst, &n, " more data");
printf("%s : %d\n", dst, n);
*dst = 0; // zero-term the string
strcat_ex(&dst, &n, "after empty");
printf("%s : %d\n", dst, n);
strcat_ex(&dst, &n, " and more");
printf("%s : %d\n", dst, n);
strcat_ex(&dst, &n, " and still more");
printf("%s : %d\n", dst, n);
}
Output
some string : 12
some string more data : 22
after empty : 22
after empty and more : 22
after empty and more and still more : 36
Your Test
Running your test program yields the following:
src="World!";
dest=NULL;
strcat_ex(&dest, &n, src);
--> gives World! with n=7
Then strcat_ex(&dest, &n, "") yields --> gives World! with n=7
Then strcpy(dest,"abc"); strcat_ex(&dest, &n, "def") yields --> gives abcdef with n=7
If *dest is NULL, you will be calling strcat on buffer when buffer has just been malloc'd and it is uninitialized. Either set buffer[0] to 0, or use calloc.
Also, you allocate *dest and then set *dest to buffer two lines later, which leaks some memory. There is no need for the first assignment to *dest.
I'm trying to make a simple version of getline. It should read a line in from stdin, reallocating the size of the buffer as necessary. It should also return the number of characters read. It takes a char ** so that the reallocated buffer can be later freed. Why am I getting a segfault?
Heres my version:
int get_input_line(char **buff, int start_size) {
char c;
int stop = 0, length = 0, k = start_size;
while(!stop) {
if(length > k) {
k += 50;
buff = (char *)(realloc(buff, start_size + 1));
}
c = getchar();
if(c == '\n'){
stop = 1;
}
buff[length] = c;
length++;
}
return length;
}
And here's the call:
char *buff = (char *)(malloc(50 + 1));
get_input_line(&buff, 50);
printf("%s", buff);
You probably meant:
*buff = (realloc(*buff, new_size));
^ ^
And
(*buff)[length] = c;
You're also missing the 0 terminator.
EDIT
As nos points out, length > k should be length >= k .
You're not detecting EOF reliably. You need to save the result of getchar() in an int and not a char. And you should not try to store EOF in your buffer.
You're not checking your memory allocations.
You're not null terminating the output string, so the printf() in main() may crash.
You're confusing someone (maybe me, maybe the compiler, maybe yourself) by allocating 51 bytes and telling the function that it only has 50 bytes to play with.
And, most particularly, you need to be using *buff at most points inside the function, including, in particular, when adding a character:
(*buff)[length++] = c;
You really should be paying more attention to all those compiler warnings. If your compiler isn't giving you any, get a better compiler (or turn on the warning flags - but you should be being shrieked at by the compiler in its default mode).
Also, you are miscalling realloc() on three grounds. One is the *buff issue. The second is that you want the size to be k, not start_size + 1. The other is that you are assigning the result to the input parameter. This is a 'no-no' because if the allocation fails, you've lost your pointer to the previously (and still) allocated data. Always use the idiom:
void *new_data = realloc(old_data, new_size);
if (new_data == 0)
...deal with out of memory error...
else
{
old_data = new_data;
old_size = new_size;
}
Applied to your code, that means:
char *new_buff = (char *)realloc(*buff, k); // NOT start_size+1!!!
if (new_buff == 0)
...deal with out of memory error...
else
*buff = new_buff;
There are those who argue against the cast on malloc() and realloc() and calloc(); there are those who prefer the casts present. There are arguments on both sides, of differing degrees of validity. I prefer the cast - I respect those who prefer no cast. We reach our different conclusions for different reasons.
I have not studied the code for other 'off-by-one' errors. I suspect that there may be several of those, too.
The line
buff = (char *)(realloc(buff, start_size + 1));
should be
*buff = (char *)(realloc(*buff, k + 1));
Also
buf[length] = c
should be
*buf[length] = c
Moreover, I think you forgot to store a final '\0'.
I'm having a segmentation fault when I want to save a string in a dynamic array.
I have a program that does this:
User insert char "s"
The program enters a loop and save strings in an array (name: cod).
When user inserts char "t", it stops
After that I save that array in the first position of a new dynamic array (name: vec).
Then if user insert char "s" again
The program enters a loop and save strings in an array.
When user inserts char "t", it stops
After that I save that array in the second position of a new dynamic array.
and so one.
This is my code:
int main(){
char Cod[30][11];
char tmp[11];
char ***vec;
int i = 0;
strcpy (tmp, "p");
vec = (char *** ) malloc (sizeof ( char *) );
vec[0] = (char ** ) malloc (sizeof ( char *) * 30);
do {
scanf("%s", tmp);
while( (strcmp (tmp, "p")) != 0){
strcpy ( Cod[i] , tmp );
scanf("%s", tmp);
i++;
}
vec = (char ***) realloc (vec, sizeof ( char *) * (i + 1));
vec[i + 1] = (char ** ) realloc (vec[i + 1], sizeof ( char *) * (30));
vec[i-1] = (char **) Cod;
scanf("%s", tmp);
}
while((strcmp (tmp, "s")) == 0);
printf("%s", vec[0][0]);
return 0;
}
This is the part of the code that work's:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char Cod[30][11];
char tmp[11];
int i = 0;
strcpy (tmp, "p");
do {
scanf("%s", tmp);
while( (strcmp (tmp, "p")) != 0){
strcpy ( Cod[i] , tmp );
scanf("%s", tmp);
i++;
}
scanf("%s", tmp);
}
while((strcmp (tmp, "s")) == 0);
printf("%s", Cod[0]);
return 0;
}
I started to fix this code but soon realized there is so much wrong in it, I don't know where to start. So instead, this turned out to be a code review answer instead, I apologize up front if it is detailed and picky.
There's a rule of thumb in C saying that if you need more than two levels of pointer indirection, your code is obfuscated and should be rewritten (reference MISRA-C:2004 17.5).
It doesn't make sense whatsoever to use dynamic memory allocation in this case, because you already know at program start-up that no string will be larger than 11 characters and there will not be more than 30 strings. If this condition is not true, you need to write a safer input method, preferably with fgets() which is safe against buffer overflows. Make sure that the input doesn't go out of bounds of array "Cod". You can allocate 30*11=330 bytes statically without a guilty conscience. And it will make the code faster.
It doesn't make sense to have 3 levels of indirection for an array of strings. You aren't even using the dynamic memory to hold a copy of strings, you just allocate pointers. This doesn't make any sense at all. If you need a pointer lookup table pointing at Cod then allocate it statically, it will only require sizeof(char*)*30 bytes.
As already mentioned, you can only use realloc on a pointer that has previously been malloc/calloc:ed.
As already mentioned, never typecast the result of malloc/realloc in C. This is C++ practice. In C, it destroys type safety and hides type compatibility bugs. There are countless, detailed discussions about this here on SO if you want to know the details.
What if you don't find "p" in the user string? The program will go havoc.
Don't name variables that affect fundamental program functionality to abstract things like tmp, vec etc. tmp could be renamed to input_buf or something, etc.
Avoid magic numbers in code, use const or #define for array length constants.
You can initialize strings in C, there is no need for strcpy to do so. char input_buf[INP_BUF_N] = "p";
To search for a char in a string, use strchr().
You shouldn't need to have the user inputting the same thing twice with scanf() in the outer do-while loop, likely a typo bug.
You can not do wild typecast between a static array of arrays to a pointer-to-pointer. This depends on the structure of whatever the pointer-to-pointer points at. Because a typical dumb-school-book dynamic 2D-array (malloc(Xsizeof(char)... malloc(Y*sizeof(char)) will not allocate memory adjacently. Plenty of discussions about this here on SO.
(you can allocate dynamic 2D arrays in adjacent memory with the use of array pointers or with "mangling", but those are rather advanced topics)
free() the dynamic memory once you are done using it.
As you hopefully can tell, the wise choice here is to rewrite this code from scratch.
Since it is a homework I tried to rewrite your code in something that should work...
char Cod[30][11];
char tmp[11];
char ***vec;
int i = 0;
strcpy (tmp, "p");
vec = (char***)malloc(sizeof(char *));
vec[0] = (char**)malloc(sizeof(Cod));
do {
scanf("%s", tmp);
int j = 0;
while(strcmp(tmp, "p")) {
strcpy(Cod[j], tmp);
scanf("%s", tmp);
j++;
}
vec = (char ***)realloc(vec, sizeof(char *) * (i+1));
vec[i] = (char **)malloc(sizeof(Cod));
memcpy(vec[i], Cod, sizeof(Cod));//you need to copy results since next time the Cod will be rewritten
scanf("%s", tmp);
i++;
} while((strcmp(tmp, "s")) == 0);
for three stars
char ***vec;
you need 3 mallocs (the casts are, at best, redundant in C and may hide an error)
vec = malloc(sizeof *vec);
vec[0] = malloc(sizeof *vec[0]);
vec[0][0] = malloc(30 * sizeof *vec[0][0]); /* sizeof (char) is 1 by definition */
In this 2 lines:
vec = (char ***) realloc (vec, sizeof ( char *) * (i + 1));
vec[i + 1] = (char ** ) realloc (vec[i + 1], sizeof ( char *) * (30));
If i = 1, then you reserve to "vec" i+1=2 pointers. In the second line you then call the 3rd one (vec[i+1] = vec[2] is 3rd element in table of size 2).