Changing strings to upper within a double pointer array - c

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 = &copy[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 = &copy[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++;
}

Related

Passing char by reference

The problem is that I am trying to pass a sentence by reference to change something about it(this case adding a character), but nothing is changed.
The first thing I tried was this original code but without putting in any "*" or "&" and I got the same output. I have read other similar questions that have used strcpy() but I am not sure how that might apply to this problem or what the solution might be as I am unfamiliar with pointers used in this way.
char my_char_func(char *x)
{
return x+'c';
}
int main()
{
char (*foo)(char);
foo = &my_char_func;
char word[]="print this out";
puts(word);
foo(&word);
puts(word);
return 0;
}
I am expecting the second output to be "print this outc"
You're adding the character c to the actual pointer. As you can't dynamically expand your character array in C, I believe you're going to have to malloc a new array with space for the extra character, delete the pointer being passed in, and then set it the beginning of the new array. That should avoid memory overrun.
int main()
{
char (*foo)(char);
int i = 0;
foo = &my_char_func;
char word[]="print this out";
for(i = 0; i < size_of(word); ++i)
{
word[i] = toupper(word[i]);
}
puts(word);
foo(&word);
puts(word);
return 0;
}
If you don't want to use toUpper, you can change you function in either of two ways:
Option 1:
void my_char_func(char *string, int sizeOfString)
{
int i = 0;
for(i = 0; i < sizeOfString; ++i)
{
//Insert logic here for checking if character needs capitalization
string[i] = string[i] + ' ';
}
}
Option 2:
Do the same as with toUpper, simply calling your own function instead.

Appending char to C array

I have a string declared as such:
char *mode_s = (char *)calloc(MODE_S_LEN, sizeof(char));
How can I add a char to the end of the array?
Lets assume " first available position " means at index 0.
char *mode_s = (char *)calloc(MODE_S_LEN, sizeof(char));
*mode_s='a';
To store a character at an arbitrary index n
*(mode_s+n)='b';
Use pointer algebra, as demonstrated above, which is equivalent to
mode_s[n]='b';
One sees that the first case simply means that n=0.
If you wish to eliminate incrementing the counter, as specified in the comment bellow, you can write a data structure and a supporting function that fits your needs. A simple one would be
typedef struct modeA{
int size;
int index;
char *mode_s;
}modeA;
The supporting function could be
int add(modeA* a, char toAdd){
if(a->size==a->index) return -1;
a->mode_s[index]=toAdd;
a->index++;
return 0;
}
It returns 0 when the add was successful, and -1 when one runs out of space.
Other functions you might need can be coded in a similar manner. Note that as C is not object oriented, the data structure has to be passed to the function as a parameter.
Finally you code code a function creating an instance
modeA genModeA(int size){
modeA tmp;
tmp.mode_s=(char *)calloc(size, sizeof(char));
tmp.size=size;
tmp.index=0;
return tmp;
}
Thus using it with no need to manually increment the counter
modeA tmp=genModeA(MODE_S_LEN);
add(&tmp,'c');
There is no standard function to concatenate a character to a string in C. You can easily define such a function:
#include <string.h>
char *strcatc(char *str, char c) {
size_t len = strlen(str);
str[len++] = c;
str[len] = '\0';
return str;
}
This function only works if str is allocated or defined with a larger size than its length + 1, ie if there is available space at its end. In your example, mode_s is allocated with a size of MODE_S_LEN, so you can put MODE_S_LEN-1 chars into it:
char *mode_s = calloc(MODE_S_LEN, sizeof(*mode_s));
for (int i = 0; i < MODE_S_LEN - 1; i++) {
strcatc(mode_s, 'X');
}
char newchar = 'a'; //or getch() from keyboard
//realloc memory:
char *mode_sNew = (char *)calloc(MODE_S_LEN + 1, sizeof(char));
//copy the str:
srncpy(mode_sNew, mode_s, MODE_S_LEN);
//put your char:
mode_sNew[MODE_S_LEN] = newchar;
//free old memory:
free(mode_s);
//reassign to the old string:
mode_s = mode_sNew;
//in a loop you can add as many characters as you want. You also can add more than one character at once, but assign only one in a new position

loop to reverse string in C

So I've looked around on SO and can't find code that answers my question. I have written a function that is supposed to reverse a string as input in cmd-line. Here is the function:
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string)];
for (x = strlen(string) - 1; x > 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
string = line;
}
When I call my reverse() function, the string stays the same. i.e., 'abc' remains 'abc'
If more info is needed or question is inappropriate, let me know.
Thanks!!
You're declaring your line array one char shorter remember the null at the end.
Another point, it should be for (x = strlen(string) - 1; x >= 0; x--) since you need to copy the character at 0.
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string) + 1];
for (x = strlen(string) - 1; x >= 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
for(x = 0; x < strlen(string); x++)
{
string[x] = line[x];
}
}
Note that this function will cause an apocalypse when passed an empty string or a string literal (as Bobby Sacamano said).
Suggestion you can probably do: void reverse(char source[], char[] dest) and do checks if the source string is empty.
I think that your answer is almost correct. You don't actually need an extra slot for the null character in line. You just need two minor changes:
Change the assignment statement at the bottom of the procedure to a memcpy.
Change the loop condition to <-
So, your correct code is this:
void reverse (char string[]) {
int x;
int i = 0;
char line[strlen(string)];
for (x = strlen(string) - 1; x >= 0; x--) {
char tmp = string[x];
line[i] = tmp;
i++;
}
memcpy(string, line, sizeof(char) * strlen(line));
}
Since you want to reverse a string, you first must decide whether you want to reverse a copy of the string, or reverse the string in-situ (in place). Since you asked about this in 'C' context, assume you mean to change the existing string (reverse the existing string) and make a copy of the string in the calling function if you want to preserve the original.
You will need the string library
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Array indexing works, and this version takes that approach,
/* this first version uses array indexing */
char*
streverse_a(char string[])
{
int len; /*how big is your string*/
int ndx; /*because 'i' is hard to search for*/
char tmp; /*hold character to swap*/
if(!string) return(string); /*avoid NULL*/
if( (len=strlen(string)) < 2 ) return(string); /*one and done*/
for( ndx=0; ndx<len/2; ndx++ ) {
tmp=string[ndx];
string[ndx]=string[len-1-ndx];
string[len-1-ndx]=tmp;
}
return(string);
}
But you can do the same with pointers,
/* this is how K&R would write the function with pointers */
char*
streverse(char* sp)
{
int len, ndx; /*how big is your string */
char tmp, *bp, *ep; /*pointers to begin/end, swap temporary*/
if(!sp) return(sp); /*avoid NULL*/
if( (len=strlen(bp=sp)) < 2 ) return(sp); /*one and done*/
for( ep=bp+len-1; bp<ep; bp++, ep-- ) {
tmp=*bp; *bp=*ep; *ep=tmp; /*swap*/
}
return(sp);
}
(No, really, the compiler does not charge less for returning void.)
And because you always test your code,
char s[][100] = {
"", "A", "AB", "ABC", "ABCD", "ABCDE",
"hello, world", "goodbye, cruel world", "pwnz0r3d", "enough"
};
int
main()
{
/* suppose your string is declared as 'a' */
char a[100];
strcpy(a,"reverse string");
/*make a copy of 'a', declared the same as a[]*/
char b[100];
strcpy(b,a);
streverse_a(b);
printf("a:%s, r:%s\n",a,b);
/*duplicate 'a'*/
char *rp = strdup(a);
streverse(rp);
printf("a:%s, r:%s\n",a,rp);
free(rp);
int ndx;
for( ndx=0; ndx<10; ++ndx ) {
/*make a copy of 's', declared the same as s[]*/
char b[100];
strcpy(b,s[ndx]);
streverse_a(b);
printf("s:%s, r:%s\n",s[ndx],b);
/*duplicate 's'*/
char *rp = strdup(s[ndx]);
streverse(rp);
printf("s:%s, r:%s\n",s[ndx],rp);
free(rp);
}
}
The last line in your code does nothing
string = line;
Parameters are passed by value, so if you change their value, that is only local to the function. Pointers are the value of the address of memory they are pointing to. If you want to modify the pointer that the function was passed, you need to take a pointer to that pointer.
Here is a short example of how you could do that.
void reverse (char **string) {
char line = malloc(strlen(*string) + 1);
//automatic arrays are deallocated once the function ends
//so line needs to be dynamically or statically allocated
// do something to line
*string = line;
}
The obvious issue with this is that you can initialize the string with static memory, then this method will replace the static memory with dynamic memory, and then you'll have to free the dynamic memory. There's nothing functionally wrong with that, it's just a bit dangerous, since accidentally freeing the string literal is illegal.
char *test = "hello";
reverse(test);
free(test); //this is pretty scary
Also, if test was allocated as dynamic memory, the pointer to it would be lost and then it would become a memory leak.

Does using calloc inside function, change the pointer passed as function argument

I do not understand why the second printf loop outputs different data than the first printf loop that was done inside the function scope. Can it be that the pointer is changed somehow inside the function so that when it returns it returns a different value?
Output:
First printf inside function:
Parts TMP|01245
Parts X|40001
Parts Y|98760
Second printf outside function, in main:
It returns jiberish and not the same as when printing inside the function.
I tried to fprintf so that I can quickly paste the results in here ,but then I received an uninformative call stack error.
#include <stdio.h>
#include <stdlib.h>
#include "string.h"
void ProtocolParse_Start(int *numParts,char **parts, char *str, const char* partsDelim )
{
int partCount = strChrCount(str,'~');
*numParts = partCount;
parts = (char**)calloc(partCount,sizeof(char));
char *tempPart;
tempPart = strtok (str,partsDelim);
parts[0] = (char*)calloc(strlen(tempPart),sizeof(char));
strcpy(parts[0],tempPart);
int i =1;
for(; i < partCount; i++)
{
tempPart = strtok (NULL, partsDelim);
parts[i] = (char*)calloc(strlen(tempPart),sizeof(char));
strcpy(parts[i],tempPart);
}
i =0;
for(; i < partCount; i++)
{
printf ("%Parts %s\n",parts[i]);
}
}
void ProtocolParse_End(int numParts,char **parts)
{
int i = 0;
for (; i < numParts; i++)
free (parts[i]);
free (parts);
}
int main()
{
char proto[32] = "TMP|01245~X|40001~Y|98760~";
char **parts;
int numParts;
ProtocolParse_Start(&numParts, parts,proto,"~");
int i =0;
for(; i < numParts; i++)
{
printf ("%Parts %s\n",parts[i]);
}
ProtocolParse_End(numParts,parts);
return 0;
}
Can anyone please shed some light onto my problem. Because I am not sure what I'm doing wrong ??
The assignment of parts inside the function has no effect on the char **parts from main. In order to modify it, you need to pass a pointer to parts, and add an extra level of indirection (yes, you'd get three asterisks now).
The code that partitions the data to strings is incorrect, too: you need to allocate an array of character pointers, and then copy each token into that array individually.
void ProtocolParse_Start(int *numParts, char ***parts, char *str, const char* partsDelim )
{
int partCount = strChrCount(str,'~');
*numParts = partCount;
*parts = malloc(partCount * sizeof(char*));
char *tempPart;
tempPart = strtok (str,partsDelim);
(*parts)[0] = malloc(strlen(tempPart)+1);
strcpy((*parts)[0], tempPart);
int i =1;
for(; i < partCount; i++)
{
tempPart = strtok (NULL, partsDelim);
(*parts)[i] = malloc(strlen(tempPart)+1);
strcpy((*parts)[i],tempPart);
}
i =0;
for(; i < partCount; i++) {
printf ("%Parts %s\n", (*parts)[i]);
}
}
I made three changes to your code:
Replaced calloc with malloc: you initialize every element anyway, so there is no reason to zero-fill the block
Removed casts in front of malloc - this is not necessary in C
Added one to strlen(tempPart) - you need this for null terminated strings.
There are different mistakes:
When you pass a parameter to a function it is always copied.
You gave a char **parts and it is copied.
Inside the function you overwrite the copied input with the new address of the calloced pointer.
Consider an easier example:
void doSomething(int a){
a=5;
}
///...
int b = 6;
doSomething(b);
When you call doSomething(b), your b is not changed.
If you want it to be changed, you have to pass a pointer to b.
void doSomething(int* a){
*a=5;
}
///...
int b = 6;
doSomething(&b);
The same is with your char*array.
You have char** partsin your main, that you want to be set to the allocated array.
so you have to pass its pointer. and write the obtained address to the dereferenced pointer.
The other big mistake is, that you gibe the wrong soze to the first calloc. It should be sizeof(char*).
Your routine should start like this:
void ProtocolParse_Start(int *numParts,char ***parts, char *str, const char* partsDelim )
{
int partCount = strChrCount(str,'~');
*numParts = partCount;
*parts = (char**)calloc(partCount,sizeof(char*));
char *tempPart;
//...
(all further accesses to parts in the function have to dereference parts)
and the call have to look like:
ProtocolParse_Start(&numParts, &parts,proto,"~");

Copying Array of Char * Corrupts Data

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.

Resources