I'm relatively new to C in general and I'm having a problem with some code. It's pretty simple code: The objective of the code is to copy a given array of char pointers, or char **source, in other words, to a given char **destination.
The issue I'm having is that sometimes (usually when I have more than 2 strings in source) the first element gets completely corrupted and when I end up printing out destination, it will print out something like ";#?" for the first element, with the other elements printing fine.
The code that performs the copying is:
void CopyArrayOfStrings(char **source, int numStrings)
{
char **destination = malloc(numStrings);
for (int i = 0; i < numStrings; i++)
{
destination[i] = malloc(strlen(source[i] + 1);
strcpy(destination[i], source[i]);
}
}
Note that I left out the code that checks if the result of malloc is NULL.
You need to change your allocation of destination as :
char **destination = malloc(numStrings*(sizeof(char*)));
to allocate number of char * pointers to hold strings.
Also verify you are appropriately passing char ** as source array of strings.
You're doing it wrong.
void CopyArrayOfStrings(char **source, int numStrings)
{
char **destination = malloc(numStrings * sizeof(char *));
for (int i = 0; i < numStrings; i++)
{
destination[i] = malloc(strlen(source[i]) + 1);
strcpy(destination[i], source[i]);
//alternatively you can use strdup() as suggested by #Christoffer
}
}
This will give you storage space for numStrings arrays. Each element of which, will point to a null-terminated string.
Related
I have an array of strings that is dynamically sized (I won't know the size of the strings at compile) that keeps giving me a segmentation fault error. The array is contained in a struct called hm and it has an array for the strings as well as an array for values. This part of the code is only to resize the string array properly when a new string is added to the struct.
I am relatively new to C and structs, so if there is a better way to implement this I would love to hear about it. I have already tried looking around for this situation and most seem to be having the issue with the outer array using sizeof(char) instead of sizeof(char*), but when I changed that the issue still happens.
//problematic part of the function
char** t = (char**)realloc(hm->keys, hm->size * sizeof(char*));
if (t) hm->keys = t;
for (i = 0; i < hm->size; i++) {
char* temp = (char*)realloc(hm->keys[i], hm->largestKey * sizeof(char)); //seg fault here
if (temp) {
hm->keys[i] = temp;
}
}
//struct
typedef struct HM_struct {
size_t size;
size_t largestKey;
char** keys;
int* values;
void (*add)(struct HM_struct* hm, char* key, int value);
} HM;
The problem is that when you realloc() and increase the allocated memory size, the new memory is not initialised (or with a debug library, initialised to a sentinal value). So, assuming you know the oldSize, a quick fix is:
char** t = realloc(hm->keys, hm->size * sizeof(char*)); // As before
if (t) hm->keys = t; // As before
for (i = oldSize; i < hm->size; i++)
hm->keys[i] = NULL;
Now, according to the realloc() definition, when you call:
char* temp = realloc(NULL, hm->largestKey * sizeof(char));
It behaves as:
char* temp = malloc(hm->largestKey * sizeof(char));
I have these functions
char *hash(char *stringa, char *tipohash) {
if (strcmp(tipohash, "md5") == 0) {
stringa = md5(stringa);
}
return stringa;
}
char *md5(char *stringa) {
unsigned char risultato[MD5_DIGEST_LENGTH];
int i;
char *hashfinale = malloc(sizeof(char) * MD5_DIGEST_LENGTH * 2);
MD5((const unsigned char *)stringa, strlen(stringa), risultato);
for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
sprintf(hashfinale + 2 * i, "%02x", risultato[i]);
}
return (char *)hashfinale;
}
How I can return (char *)hashfinale doing the free without losing the value of the string?
This is the caller
char *hashlinea = hash(stringa, hashType);
There are basically two ways to solve the problem, and none of them involves your code calling free.
The first way is to just do nothing different from now, except to add documentation so the user of your hash function knows that the code must call free on the returned pointer:
// This is the code using your function
char *hashlinea = hash(stringa,hashType);
// Some code using hashlinea
free(hashlinea);
The second way is to pass a pointer to an existing array, and your code use that array instead of allocating it using malloc:
char hashlinea[MD5_DIGEST_LENGTH*2];
hash(stringa, hashType, hashlinea);
For this your hash function needs to pass on the third argument to the md5 function, which should use it instead of allocating memory:
char *md5(char *stringa, char *hashfinale){
unsigned char risultato[MD5_DIGEST_LENGTH];
int i;
// No memory allocation here
MD5((const unsigned char *)stringa, strlen(stringa), risultato);
for(i = 0; i < MD5_DIGEST_LENGTH; i++) {
sprintf(hashfinale + 2*i,"%02x",risultato[i]);
}
return hashfinale;
}
It is not possible. IMO it is better to pass the pointer to the buffer. The caller will be responsible for the memory management
char *md5(char *stringa, char *hashfinale){
...
}
There is a problem in your md5 function: the size allocated for the MD5 hash must be one byte longer for the null terminator:
char *hashfinale = malloc(sizeof(char) * (MD5_DIGEST_LENGTH * 2 + 1));
Note that in C (and C++) sizeof(char) is 1 by definition, so you could just write:
char *hashfinale = malloc(MD5_DIGEST_LENGTH * 2 + 1);
Regarding your question, hash returns either its argument or an allocated object. This is a problem for memory management, as yo may not know later in the program if the return value must be freed or not. Passing the destination array for the hash string is a better alternative, otherwise you should duplicate the string so the return value of hash can be unconditionally freed:
char *md5(const char *stringa) {
unsigned char risultato[MD5_DIGEST_LENGTH];
int i;
char *hashfinale = malloc(MD5_DIGEST_LENGTH * 2 + 1);
MD5((const unsigned char *)stringa, strlen(stringa), risultato);
for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
sprintf(hashfinale + 2 * i, "%02x", risultato[i]);
}
return hashfinale;
}
// always free the return value
char *hash(const char *stringa, const char *tipohash) {
if (!strcmp(tipohash, "md5")) {
return md5(stringa);
} else {
return strdup(stringa);
}
}
I have created double pointer char to be used as a 2d array to store strings. The append function is meant to add the string provided to the end of the array, the num_strings pointer is provided to keep track of the elements in the array (since I can't use sizeof). It seems that at some point, the function isn't allocating enough memory but I can't seem to figure out where and can't find any other issues.
I have already tried giving both the outer array and the inner array large amounts of memory, much more than they need. The issue persists. I have also tried copying the string to the array after the function had run.
int main(int argc, char *argv[]) {
char **strings = NULL;
int num_strings = 0;
append(&strings, &num_strings, "Alex");
append(&strings, &num_strings, "Edward");
// Do things with array
for (int i = 0; i < num_strings; i++) {;
printf("%s\n", strings[i]);
}
// Free memory after use
for (int i = 0; i < num_strings; i++) {
free(strings[i]);
}
free(strings);
strings = NULL;
return 0;
}
void append(char ***array, int * num_strings, char *string) {
if (*array == NULL) {
*array = malloc(sizeof(*array)); // start with enough room for 1 item (pointer)
} else {
// reallocate memory for new item
*array = realloc(*array, (((*num_strings) + 1) * sizeof(*array)));
}
printf("Char Size: %lu\n", sizeof(char));
printf("Given Size: %lu\n", sizeof(***(array)));
*(array[*num_strings]) = malloc((strlen(string) + 1) * sizeof(***(array + 0)));
strcpy(*(array[*num_strings]), string);
(*num_strings)++; // increment the number of strings
}
The output of the program should be the two strings, at the moment it only prints the first and then crashs due to the segmentation fault.
The problem is there are a couple instances of *(array[*num_strings]) that should be (*array)[*num_strings].
The difference is that the first form tries to index through the pointer passed to the function, as if the passed strings were an array, corrupting the caller's stack. The corrected version first derefernces the pointer, then indexed through the target as desired.
There are also a few places where sizeof(*array) is used where it should be sizeof(**array). x = malloc(sizeof(x)) is never correct. But this isn't causing a visible problem.
I need to convert arguments given at command line such as: $ myprogram hello world
and the words need to be printed in CAPS. I am able to to do everything except access the double pointer array to make the changes with toupper()
static char **duplicateArgs(int argc, char **argv)
{
char **copy = malloc(argc * sizeof (*argv));
if(copy == NULL){
perror("malloc returned NULL");
exit(1);
}
int i;
for(i = 0; i<argc; i++){
copy[i] = argv[i];
}
char **temp;
temp = ©[1];
*temp = toupper(copy[1]);
return copy;
}
*temp = toupper(copy[1]);
toupper converts a single character, if you want to convert an entire string:
char *temp = copy[1]; /* You don't need a double pointer */
size_t len = strlen(temp);
for (size_t i = 0; i < len; i++) {
temp[i] = toupper(temp[i]);
}
I assume the argument that is passed into your function char **argv is passed directly from main, so it represents a pointer to the beginning of an array of pointers to each of the command line arguments.
argc represents the number of command line arguments.
Inside your function, you create a new buffer, and then copy the contents of argv into it. So you are creating a copy of the array of pointers to the command line arguments, NOT the command line argument strings themselves.
I am guessing you intended to copy the strings, rather than the pointers to the strings (what would be the point of that?). I suggest you look into the functions strdup and/or strncpy to copy the actual strings.
This also explains with the 'toupper' does not work as you expect - instead of passing a single character to it, you are passing a pointer to a null terminated string of characters.
From the man page of toupper() the function prototype is
int toupper(int c);
In your code, the argument copy[1] is not an int value.
Instead what you want is to check each and every element, if they are in lower case, convert them to upper case. A pseudo-code will look like
for(i = 0; i<argc; i++){
copy[i] = malloc(strlen(argv[i])+ 1); //allocate memory
for (j = 1; j < argc; j++)
for (i = 0; i < strlen(argv[j]); i++)
{
if (islower(argv[j][i])) //check if it is lower case
copy[j-1][i] = toupper(argv[j][i]);
else
copy[j-1][i] = argv[j][i]; //do not convert
}
Consider this example:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
static char **duplicateArgs(int argc, char **argv)
{
char **copy = NULL;
// allocate memry for pointers to new lines
copy = (char **)malloc(sizeof(char *) * argc);
int line, chr;
for(line = 0; line < argc; line++)
{
// allocate memory for new line
copy[line] = (char *)malloc(sizeof(char) * (strlen(argv[line]) + 1));
// copy with changes
for(chr = 0; chr <= strlen(argv[line]); chr++)
{
copy[line][chr] = toupper(argv[line][chr]);
}
}
return copy;
}
int main(int argc, char * argv[])
{
char ** strs;
int i;
strs = duplicateArgs(argc, argv);
for(i = 0; i < argc; i++)
{
printf("%s\n", strs[i]);
}
return 0;
}
EDIT:
Also you can make a decision about using argv[0] (name of executable file) and change a code if you need. Also checking of malloc result can be added, and other improvements... if you need :-)
You are running into an error using the toupper() function because you are trying to pass in a string instead of an individual letter. Here is an excerpt from the man page describing the function:
DESCRIPTION
The toupper() function converts a lower-case letter to the corresponding
upper-case letter. The argument must be representable as an unsigned
char or the value of EOF.
You have a pointer to a pointer which you could visulize as something like this. In C a string is just an array of chars so you need to dereference twice to get the data in the second level of arrays (the individual letter). Every time you add an * you can think of it as removing one layer of pointers. And you can think of the * operator as the inverse of the & operator.
This line is your problem line
temp = ©[1];
try this instead
//This is a pointer to an individual string
char *temp = copy[1];
//Keep going while there are letters in the string
while(*temp != NULL) {
//convert the letter
toupper(*temp);
//Advance the pointer a letter
temp++;
}
In the main method , i am creating an array of pointers to string
in the add method i am reallocating the array size and adding x elements which i dont know
while coming back to main how can i know the new size of the array , i mean the number of elements int the array ?
Here is my code .. (it has some bugs)
#include <stdio.h>
void add(char ***x);
int main()
{
char **s;
s = (char **) malloc(sizeof(char *));
int i;
add(&s);
for( i=1;i<=?????(**find the new size of the array** );i++)
puts(*s[i]);
return 0;
}
void add(char ***x)
{
- ** // alter arry add x random datas to the array of string pointer**
/*
s[1]="Hello";
s[2]="Need";
s[3]="a help";
s[4]="and help";
s[5]="for the help";
*/
char **data;
int i = 0;
for (i = 1; i <= 5; i++)
{
data = (char **) realloc(*x, 1 * sizeof(char *));
data[i] = (char *) malloc(i * sizeof(char *));
strcpy(data[i], "first");
}
}
can some one please point and fix the bug in the code..
(Sidenote:
can some one please point and fix the bug in the code..
hey, isn't that what debuggers are for?)
Long story short, keep track of it manually:
char **func_that_reallocs(char **ptr, size_t *sz)
{
char **tmp = realloc(ptr, new_size * sizeof(*ptr));
*sz = new_size;
return tmp;
}
And please do not cast the return value of malloc()!
Always add one entry more to the array as needed and set this additional last entry to NULL.
Then write a function which scans the array until it find this NULL-pointer and return the number of entries counted up until then and you are done.
It's the same concept as for a C-"string", with the only difference of using a NULL instead of '\0' as (array-)terminator.
Some people call this last element also the "stopper"-element.
The positive thing about this approach is, one does not have to keep the array's size in a different variable, which might get out of sync with the real size of the array: The size is implicitly given by the array itself.