I am working on a queue data structure. The structure is:
struct queue
{
char array[MAX_LENGTH][8];
int back;
};
It is designed to store a list of MAX_LENGTH strings that are 7 chars long.
I wish to push a 1D array of 8 chars (well, 7 chars and \0, just like the array in the struct).
I have this push code:
void push (struct queue *q, char s[]){
q->array[q->back] = s;
}
Which I figure might work, but apparently does not. In cl (.net's C/C++) compiler, I get the following error:
2.c(29) : error C2106: '=' : left operand must be l-value
gcc returns a similar error, on the same line (but I forget, and don't have access to gcc at the moment).
I'm fairly new to structs, and pointers so there's probably something very obvious I'm not doing. Appreciate any help :)
Change it to:
void push (struct queue *q, char s[])
{
strcpy(q->array[q->back], s);
}
You can assign structs in C using = but you can't assign arrays - you have to use strcpy/memcpy for things like this.
You have to treat q->array just as you would any other array. You can't just "push" it, you have to pass the location you want to put it in then copy each character (or use q->back as your location). Something like this perhaps:
void push (struct queue *q, char s[]){
int i;
for ( i = 0; s[i]; ++i )
q->array[q->back][i] = s[i];
q->array[q->back][i] = '\0';
}
Or use strcpy:
void push (struct queue *q, char s[]){
strcpy(q->array[q->back], s);
}
If you want a bounds-checked strcpy using only the standard C library functions, then strncat is actually your best bet:
void push (struct queue *q, char s[])
{
q->array[q->back][0] = 0;
strncat(q->array[q->back], s, sizeof q->array[q->back] - 1);
}
On the other hand, if you know for certain that s always points to an array of the same size as the array in the struct (as your question states), then memcpy is the simplest solution:
void push (struct queue *q, char s[])
{
memcpy(q->array[q->back], s, sizeof q->array[q->back]);
}
strncpy(q->array[q->back], s,MAX_LENGTH) could be better to avoid buffer overflow
Related
I can understand pointers to a certain extent but the multiple layers of dereferencing in swap() is confusing me. Thus,I'm unable to implement it correctly.
Below is a code on alphabetizing based on insertion sort:
void insertionSort(char **array,int rows,int cols)
{
for(int i=1;i<=rows-1;i++)
{
for(int j=i-1;strcmp(array[i],array[j])<0 && j>=0;j--)
{
swap(&array[i],&array[j]);
i--; //when swapped, subscript of key also drops
}
}
}
void swap(char **s1,char**s2)
{
char **temp=s1;
strcpy(*s1,*s2);
strcpy(*s2,*temp);
}
I know the swap() is wrongly implemented. I hopefully would like to know how to step through the thinking process to correctly implement swap()(ie how to understand the muliple layers of dereferencing better)
You don't want to use strcpy at all. You simply want to swap two char *s, to which you have pointers (char **s). As such, the implementation should be the same as any other swap. The temporary should be a char *, and you should be swapping the char * values, not copying the strings that lie behind them.
void swap(char **s1,char **s2)
{
char *temp = *s1;
*s1 = *s2;
*s2 = temp;
}
This is exactly the same as e.g. a function for swapping ints, except with int replaced by char *.
The problem is that this code won't interchange these 2 strings. I'm new to programming but I can tell that the problem is that swap function, but I do not know how to fix it.
I tried to add strcpy instead of "=" in swap but that didn't worked.
#include <stdio.h>
#include <stdlib.h>
void swap(char *t1, char *t2) {
char *t;
t=t1;
t1=t2;
t2=t;
}
int main() {
char *s[2] = {"Hello", "World"};
swap(s[0], s[1]);
printf("%s\n%s", s[0], s[1]);
return 0;
}
You want to use out parameters here, and since your strings are represented as pointers, you need pointers to pointers:
void swap(char **t1, char **t2) {
char *t;
t = *t1;
*t1 = *t2;
*t2 = t;
}
Call it like this:
swap(&s[0], &s[1]);
I tried to add strcpy instead of "=" in swap but that didn't worked.
The reason why that doesn't work is because the strings are actually stored in the program's binary and therefore can't be modified, and with strcpy you would write over them. If you copy them to the stack or the heap instead then you can do the swap with strcpy. Of course that's going to be less efficient than just swapping the pointers, but this is how it would look like:
void swap(char *t1, char *t2) {
char buf[16]; // needs to be big enough to fit the string
strcpy(buf, t1);
strcpy(t1, t2);
strcpy(t2, buf);
}
Also you would need to change the definition of s to something akin to
char s[2][16] = { "Hello", "World" }; // strings are copied to the stack now
Check the types carefully.
What you have got as array members are pointers (to the starting element of string literals). You need to swap the members in a way so that they point to the other string literal. So, you need to change those pointers themselves.
So, you need to pass pointer to those pointers and then make the change from the called function.
Do something like
swap(&(s[0]), &(s[1]));
and then, in the called function:
void ptrSwap(char **t1, char **t2) {
char *temp;
temp=*t1;
*t1=*t2;
*t2=temp;
}
Bonus points: Name your functions (and variables, too, wherever applicable) meaningfully.
You need to passing the pointer of pointer, i.e. address of the position in array where the strings are present, so that you can swap and place correct addresses there.
Try the below code:
#include <stdio.h>
#include <stdlib.h>
void swap(char **t1, char **t2) {
char *t;
t=*t1;
*t1=*t2;
*t2=t;
}
int main() {
char *s[2] = {"Hello", "World"};
swap(&s[0], &s[1]);
printf("%s\n%s", s[0], s[1]);
return 0;
}
Output:
World
Hello
I am a beginner in C. I wanted to make strcat function using pointers. I made it but don't know what is wrong with it. I used gcc compiler and it gave segmentation fault output.
#include<stdio.h>
#include<string.h>
char scat(char *,char *);
void main()
{
char *s="james";
char *t="bond";
char *q=scat(s,t);
while(*q!='\0') printf("the concatenated string is %c",*q);
}
char *scat(char *s,char *t)
{
char *p=s;
while(*p!='\0'){
p++;
}
while(*t!='\0'){
*p=*t;
p++;
t++;
}
return p-s-t;
}
This one works:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *scat(char *,char *); /* 1: your prototype was wrong */
void main()
{
char *s="james";
char *t="bond";
char *q=scat(s,t);
printf("cat: %s\n", q); /* 2: you can use %s to print a string */
free(q);
}
char *scat(char *s,char *t)
{
char *p=malloc(strlen(s)+strlen(t)+1); /* 3: you will have to reserve memory to hold the copy. */
int ptr =0, temp = 0; /* 4 initialise some helpers */
while(s[temp]!='\0'){ /* 5. use the temp to "walk" over string 1 */
p[ptr++] = s[temp++];
}
temp=0;
while(t[temp]!='\0'){ /* and string two */
p[ptr++]=t[temp++];
}
return p;
}
You have to allocate new space to copy at the end of s. Otherwise, your while loo[ will go in memory you don't have access to.
You shoul learn about malloc() here.
It is undefined behaviour to modify a string literal and s, and eventually p, is pointing to a string literal:
char* s = "james";
s is passed as first argument to scat() to which the local char* p is assigned and then:
*p=*t;
which on first invocation is attempting to overwite the null character an the end of the string literal "james".
A possible solution would be to use malloc() to allocate a buffer large enough to contain the concatentation of the two input strings:
char* result = malloc(strlen(s) + strlen(p) + 1); /* + 1 for null terminator. */
and copy them into it. The caller must remember to free() the returned char*.
You may find the list of frequently asked pointer questions useful.
Because p goes till the end of the string and then it starts advancing to illegal memory.
That is why you get segmentation fault.
It's because s points to "james\0", string literal & you cannot modify constant.
Change char *s="james"; to char s[50]="james";.
You need to understand the basics of pointers.
a char * is not a string or array of characters, it's the address of the beginning of the data.
you can't do a char * - char* !!
This is a good tutorial to start with
you will have to use malloc
You get a segmentation fault because you move the pointer to the end of s and then just start writing the data of p to the memory directly following s. What makes you believe there is writable memory available after s? Any attempt to write data to non-writable memory results in a segmentation fault and it looks like the memory following s is not writable (which is to expect, since "string constants" are usually stored in read-only memory).
Several things look out of order.
First keep in mind that when you want to return a pointer to something created within a function it needs to have been malloc'ed somewhere. Much easier if you pass the destination as an argument to the function. If you follow the former approach, don't forget to free() it when you're done with it.
Also, the function scat has to return a pointer in the declaration i.e. char *scat, not char scat.
Finally you don't need that loop to print the string, printf("%s", string); will take care of printing the string for you (provided it's terminated).
At first, your code will be in infinte loop because of the below line. you were supposed to use curely braces by including "p++; t++ " statements.
while(*t!='\0')
*p=*t;
though you do like this, you are trying to alter the content of the string literal. which will result in undefined behavior like segmentation fault.
A sequence of characters enclosed with in double quotes are called as string literal. it is also called as "string". String is fixed in size. once you created, you can't extend its size and alter the contents. Doing so will lead to undefined behavior.
To solve this problem , you need to allocate a new character array whose size is sum of the length of two strings passed. then append the two strings into the new array. finally return the address of the new array.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
char* scat(char *,char *);
void append(char *t , char *s);
int main(void)
{
char *s="james";
char *t="bond";
char *n = scat(s,t);
printf("the concatenated string is %s",n);
return 0;
}
char* scat(char *s,char *t)
{
int len = strlen(s) + strlen(t);
char *tmp = (char *)malloc(sizeof(char)* len);
append(tmp,s);
append(tmp,t);
return tmp;
}
void append(char *t , char *s)
{
//move pointer t to end of the string it points.
while(*t != '\0'){
t++;
}
while( *s != '\0' ){
*t = *s;
t++;
s++;
}
}
I am trying to study C, and I am running in to troubles using char* and char arrays. I am using a generic hash-set container from a library (which I don't want to describe in details). This library includes the function
void *HashSetLookup(hashset *h, const void *elemAddr);
which I have to use to search in the hash set to see if the element already exists there (the hash and compare functions are part of the hashset struct). In this case I use the hashset to store pointers to C-strings, or more specifically (char * *) . My problem is that the following code gives a segmentation fault:
char word[1024];
/* Some code that writes to the word buffer */
HashSetLookup(stopList, &word);
while this code works fine (and as expected):
char word[1024];
/* The same code as before that writes to the word buffer */
char* tmp = strdup(word);
HashSetLookup(stopList, &tmp);
free(tmp);
I thought char word[] and char* were basically the same thing. The only difference being that char word[1024] is in the stack with a fixed length of 1024, but tmp in the heap occupying only as much space as necessary (strlen(word)+1).
Therefore I don't understand why I have to make a copy of the string in the heap to be able to call this function. Why does this happen? Is there some more fundamental difference between char* tmp = strdup("something") and char word[1024] = "something"?
You mention you need a char ** and there lies the problem: for an array, word and &word mean the same thing - the actual location of the array contents. The reason it works when you use a pointer is because the "pointer" is stored at a different location, while it points to the same array. You don't need an strdup, you simply need to create a pointer:
char* tmp = word;
HashSetLookup(stopList, &tmp);
Hard to tell without the documentation of HashSetLookup.
But it expects a const void * as its second parameter, so you should pass tmp, and not &tmp, because tmp is already a pointer.
I don't see need for char ** here at all.
Also, you might probably be interested in what HashSetLookup() returns.
You must have missed something in the first example because "word" is not being used at all.
Anyhow, in most of the environments, when you write 'char *s = "Hello World"', it gets created on the code segment and cannot be modified. Code segment means that it is part of the executable code which must not be modified but only read. When you try to write it, you get a seg fault.
However 'char[]' gets created on data segment so it can be modified without any problem.
Perhaps this helps:
http://www.iso-9899.info/wiki/Why_arrays_arent_pointers
http://www.lysator.liu.se/c/c-faq/c-2.html
Since you mentioned char** I think the problem is with the function trying to write to the location pointed by the second argument, i.e. when you write:
HashSetLookup( stopList, &word );
it goes and tries assigning an address to word (and that's why it needs the address of it.), which overwrites the buffer with a pointer.
This is demonstrated by the following silly snippet (keep in mind that address of array is still the address of its first element):
#include <stdio.h>
#include <stdlib.h>
void func( void* boo )
{
char** ptr = ( char** )boo;
printf( "func: got %p\n", boo );
*ptr = "bad func";
}
int main( int argc, char* argv[] )
{
char buf[128], *p;
func( &buf ); /* buf got overwritten */
printf( "array: %s\n", buf );
p = malloc( 128 );
func( &p ); /* p got new value */
printf( "malloc: %s\n", p );
return 0;
}
I have an unsorted dictionary file named "dict.txt".
I have managed to put the words of the file in an array and the qsort() I use also seems to be working just fine (That is, the array is sorted).
The problem arises when I call bsearch(),
the program crashes and my question is:
Why is this happening?
I use gcc to compile and don't use an IDE of any sort so I don't have any debugger nor do I know how to use one (yet).
I am quite aware that the code presented here might contain several problems.
That is because I am quite new to c and my background is mainly Java (which despite the similarities seems to be a drawback, because I am so used to OO and c is obviously not OO).
Any advice would be greatly appreciated.
int strcmp_mod(const void *p1, const void *p2) {
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
int main(void) {
int size, i;
char **words;
char *pItem;
char *key = "fight";
char* buf = load_file("dict.txt"); if (buf == NULL) return 1;
size = count_words(buf);
words = (char**)malloc((size+1) * sizeof(char*));
for (i=0; i<size; i++) {
words[i] = (char*)malloc(80 * sizeof(char));
}
copy_words_to_lower(buf, words, size);
words[size] = '\0';
qsort(words, size, sizeof(char*), strcmp_mod);
for (i=0; i<size; i++) {
printf("%s\n", words[i]);
}
pItem = (char *) bsearch(key, words, size, sizeof(char*), strcmp_mod);
if (pItem!=NULL)
printf ("%s is in the array.\n", pItem);
else
printf ("%s is not in the array.\n", key);
return 0;
}
Try giving bsearch the address of key.
Why is this happening?
You're passing a char* as the key parameter to bsearch, but your comparator expects the result of casting a char** to void*.
Once you've fixed that, the next problem is that the return value from bsearch is a pointer to the matching item in the array. So again a char** not a char*.
Any advice would be greatly appreciated.
Either get a debugger, or else get ready to add lots of logging to your code.
Also the construction of your words array is slightly off. As it is it gets the job done, but it might be an idea to allocate the buffer for each word as you go, rather than all the same size at the start. Who knows if somebody will send you a file with a word in it more than 80 chars long? You terminate the list of words with a nul character, '\0', when you probably mean to terminate it with a null pointer, NULL. '\0' actually works, because it's another way of saying 0, and 0 converts to a null pointer. But it's not what you mean. And the array doesn't need to be null-terminated at all right now, because every time you use it after that you specify its length, size.